diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 7310a37..d8b0f5d 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,8 +9,12 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); - lowPass->host = host; + auto *filter = new LowPass(); + filter->host = host; + effects.push_back(filter); + auto *noise = new Noise(); noise->host = host; + effects.push_back(noise); } void multiply(float *target, float *modulation, uint32_t size) { @@ -44,8 +48,9 @@ void Instrument::render(float *buffer, uint32_t count) { float *waveform = wave->render(count); - processEffect(waveform, count, lowPass); - processEffect(waveform, count, noise); + for (auto effect: effects) { + processEffect(waveform, count, effect); + } multiply(waveform, envelope->render(count), count); multiply(waveform, volume, count); add(buffer, waveform, count); @@ -54,8 +59,10 @@ void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); - lowPass->frequency = frequency; - lowPass->update(); + for (auto effect: effects) { + effect->frequency = frequency; + effect->update(); + } } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 7310a37..d8b0f5d 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,8 +9,12 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); - lowPass->host = host; + auto *filter = new LowPass(); + filter->host = host; + effects.push_back(filter); + auto *noise = new Noise(); noise->host = host; + effects.push_back(noise); } void multiply(float *target, float *modulation, uint32_t size) { @@ -44,8 +48,9 @@ void Instrument::render(float *buffer, uint32_t count) { float *waveform = wave->render(count); - processEffect(waveform, count, lowPass); - processEffect(waveform, count, noise); + for (auto effect: effects) { + processEffect(waveform, count, effect); + } multiply(waveform, envelope->render(count), count); multiply(waveform, volume, count); add(buffer, waveform, count); @@ -54,8 +59,10 @@ void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); - lowPass->frequency = frequency; - lowPass->update(); + for (auto effect: effects) { + effect->frequency = frequency; + effect->update(); + } } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 077bfe0..df45330 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -17,8 +17,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; - LowPass *lowPass = new LowPass(); - Noise *noise = new Noise(); + std::list effects; float volume = 0; void render(float *buffer, uint32_t count); diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 7310a37..d8b0f5d 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,8 +9,12 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); - lowPass->host = host; + auto *filter = new LowPass(); + filter->host = host; + effects.push_back(filter); + auto *noise = new Noise(); noise->host = host; + effects.push_back(noise); } void multiply(float *target, float *modulation, uint32_t size) { @@ -44,8 +48,9 @@ void Instrument::render(float *buffer, uint32_t count) { float *waveform = wave->render(count); - processEffect(waveform, count, lowPass); - processEffect(waveform, count, noise); + for (auto effect: effects) { + processEffect(waveform, count, effect); + } multiply(waveform, envelope->render(count), count); multiply(waveform, volume, count); add(buffer, waveform, count); @@ -54,8 +59,10 @@ void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); - lowPass->frequency = frequency; - lowPass->update(); + for (auto effect: effects) { + effect->frequency = frequency; + effect->update(); + } } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 077bfe0..df45330 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -17,8 +17,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; - LowPass *lowPass = new LowPass(); - Noise *noise = new Noise(); + std::list effects; float volume = 0; void render(float *buffer, uint32_t count); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 7ccc60b..2cc9efa 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -101,16 +101,24 @@ jfloat influence, jfloat parameter1) { Instrument *instrument = getInstrument(id); - Effect *effect; - switch (effect_number) { - case 0: - effect = instrument->lowPass; - break; - case 1: - effect = instrument->noise; - break; - } + auto iterator = instrument->effects.begin(); + std::advance(iterator, effect_number); + auto *effect = *iterator; effect->influence = influence; effect->parameter1 = parameter1; } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_moveEffects(JNIEnv *env, jobject thiz, jint id, + jint from, jint to) { + Instrument *instrument = getInstrument(id); + auto source = instrument->effects.begin(); + std::advance(source, from); + auto destination = instrument->effects.begin(); + std::advance(destination, to); + if (instrument->effects.size() == to + 1) { + destination = instrument->effects.end(); + } + instrument->effects.splice(destination, instrument->effects, source); +} } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 7310a37..d8b0f5d 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,8 +9,12 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); - lowPass->host = host; + auto *filter = new LowPass(); + filter->host = host; + effects.push_back(filter); + auto *noise = new Noise(); noise->host = host; + effects.push_back(noise); } void multiply(float *target, float *modulation, uint32_t size) { @@ -44,8 +48,9 @@ void Instrument::render(float *buffer, uint32_t count) { float *waveform = wave->render(count); - processEffect(waveform, count, lowPass); - processEffect(waveform, count, noise); + for (auto effect: effects) { + processEffect(waveform, count, effect); + } multiply(waveform, envelope->render(count), count); multiply(waveform, volume, count); add(buffer, waveform, count); @@ -54,8 +59,10 @@ void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); - lowPass->frequency = frequency; - lowPass->update(); + for (auto effect: effects) { + effect->frequency = frequency; + effect->update(); + } } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 077bfe0..df45330 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -17,8 +17,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; - LowPass *lowPass = new LowPass(); - Noise *noise = new Noise(); + std::list effects; float volume = 0; void render(float *buffer, uint32_t count); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 7ccc60b..2cc9efa 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -101,16 +101,24 @@ jfloat influence, jfloat parameter1) { Instrument *instrument = getInstrument(id); - Effect *effect; - switch (effect_number) { - case 0: - effect = instrument->lowPass; - break; - case 1: - effect = instrument->noise; - break; - } + auto iterator = instrument->effects.begin(); + std::advance(iterator, effect_number); + auto *effect = *iterator; effect->influence = influence; effect->parameter1 = parameter1; } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_moveEffects(JNIEnv *env, jobject thiz, jint id, + jint from, jint to) { + Instrument *instrument = getInstrument(id); + auto source = instrument->effects.begin(); + std::advance(source, from); + auto destination = instrument->effects.begin(); + std::advance(destination, to); + if (instrument->effects.size() == to + 1) { + destination = instrument->effects.end(); + } + instrument->effects.splice(destination, instrument->effects, source); +} } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index b9fa2c1..70d173a 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -18,7 +18,7 @@ abstract class Instrument(var name: String) { var voice: Voice = Voice(this) var envelope = Envelope(this) - val effects = Array(EffectType.VALUES.size) { + val effects = MutableList(EffectType.VALUES.size) { Effect(EffectType.VALUES[it], this) } @@ -33,6 +33,7 @@ abstract fun updateEnvelope() abstract fun updateEffects() abstract fun isPlaying(note: Note): Boolean + abstract fun moveEffects(from: Int, to: Int) companion object { val instruments = mutableListOf() diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 7310a37..d8b0f5d 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,8 +9,12 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); - lowPass->host = host; + auto *filter = new LowPass(); + filter->host = host; + effects.push_back(filter); + auto *noise = new Noise(); noise->host = host; + effects.push_back(noise); } void multiply(float *target, float *modulation, uint32_t size) { @@ -44,8 +48,9 @@ void Instrument::render(float *buffer, uint32_t count) { float *waveform = wave->render(count); - processEffect(waveform, count, lowPass); - processEffect(waveform, count, noise); + for (auto effect: effects) { + processEffect(waveform, count, effect); + } multiply(waveform, envelope->render(count), count); multiply(waveform, volume, count); add(buffer, waveform, count); @@ -54,8 +59,10 @@ void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); - lowPass->frequency = frequency; - lowPass->update(); + for (auto effect: effects) { + effect->frequency = frequency; + effect->update(); + } } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 077bfe0..df45330 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -17,8 +17,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; - LowPass *lowPass = new LowPass(); - Noise *noise = new Noise(); + std::list effects; float volume = 0; void render(float *buffer, uint32_t count); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 7ccc60b..2cc9efa 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -101,16 +101,24 @@ jfloat influence, jfloat parameter1) { Instrument *instrument = getInstrument(id); - Effect *effect; - switch (effect_number) { - case 0: - effect = instrument->lowPass; - break; - case 1: - effect = instrument->noise; - break; - } + auto iterator = instrument->effects.begin(); + std::advance(iterator, effect_number); + auto *effect = *iterator; effect->influence = influence; effect->parameter1 = parameter1; } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_moveEffects(JNIEnv *env, jobject thiz, jint id, + jint from, jint to) { + Instrument *instrument = getInstrument(id); + auto source = instrument->effects.begin(); + std::advance(source, from); + auto destination = instrument->effects.begin(); + std::advance(destination, to); + if (instrument->effects.size() == to + 1) { + destination = instrument->effects.end(); + } + instrument->effects.splice(destination, instrument->effects, source); +} } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index b9fa2c1..70d173a 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -18,7 +18,7 @@ abstract class Instrument(var name: String) { var voice: Voice = Voice(this) var envelope = Envelope(this) - val effects = Array(EffectType.VALUES.size) { + val effects = MutableList(EffectType.VALUES.size) { Effect(EffectType.VALUES[it], this) } @@ -33,6 +33,7 @@ abstract fun updateEnvelope() abstract fun updateEffects() abstract fun isPlaying(note: Note): Boolean + abstract fun moveEffects(from: Int, to: Int) companion object { val instruments = mutableListOf() diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index d24f474..40f3e97 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -78,15 +78,19 @@ ) } - fun applyEffectAttributes(effect: Effect) { + fun applyEffectAttributes(instrument: Instrument, effect: Effect) { applyEffectAttributes( id, - effect.type.ordinal, + instrument.effects.indexOf(effect), if (effect.active) effect.influence.value else 0f, - effect.parameters[0].value + effect.parameters[0]?.value ?: 0f ) } + fun moveEffects(from: Int, to: Int) { + moveEffects(id, from, to) + } + private external fun createInstrument(): Int private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) @@ -107,4 +111,6 @@ influence: Float, parameter1: Float ) + + private external fun moveEffects(id: Int, from: Int, to: Int) } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 7310a37..d8b0f5d 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,8 +9,12 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); - lowPass->host = host; + auto *filter = new LowPass(); + filter->host = host; + effects.push_back(filter); + auto *noise = new Noise(); noise->host = host; + effects.push_back(noise); } void multiply(float *target, float *modulation, uint32_t size) { @@ -44,8 +48,9 @@ void Instrument::render(float *buffer, uint32_t count) { float *waveform = wave->render(count); - processEffect(waveform, count, lowPass); - processEffect(waveform, count, noise); + for (auto effect: effects) { + processEffect(waveform, count, effect); + } multiply(waveform, envelope->render(count), count); multiply(waveform, volume, count); add(buffer, waveform, count); @@ -54,8 +59,10 @@ void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); - lowPass->frequency = frequency; - lowPass->update(); + for (auto effect: effects) { + effect->frequency = frequency; + effect->update(); + } } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 077bfe0..df45330 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -17,8 +17,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; - LowPass *lowPass = new LowPass(); - Noise *noise = new Noise(); + std::list effects; float volume = 0; void render(float *buffer, uint32_t count); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 7ccc60b..2cc9efa 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -101,16 +101,24 @@ jfloat influence, jfloat parameter1) { Instrument *instrument = getInstrument(id); - Effect *effect; - switch (effect_number) { - case 0: - effect = instrument->lowPass; - break; - case 1: - effect = instrument->noise; - break; - } + auto iterator = instrument->effects.begin(); + std::advance(iterator, effect_number); + auto *effect = *iterator; effect->influence = influence; effect->parameter1 = parameter1; } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_moveEffects(JNIEnv *env, jobject thiz, jint id, + jint from, jint to) { + Instrument *instrument = getInstrument(id); + auto source = instrument->effects.begin(); + std::advance(source, from); + auto destination = instrument->effects.begin(); + std::advance(destination, to); + if (instrument->effects.size() == to + 1) { + destination = instrument->effects.end(); + } + instrument->effects.splice(destination, instrument->effects, source); +} } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index b9fa2c1..70d173a 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -18,7 +18,7 @@ abstract class Instrument(var name: String) { var voice: Voice = Voice(this) var envelope = Envelope(this) - val effects = Array(EffectType.VALUES.size) { + val effects = MutableList(EffectType.VALUES.size) { Effect(EffectType.VALUES[it], this) } @@ -33,6 +33,7 @@ abstract fun updateEnvelope() abstract fun updateEffects() abstract fun isPlaying(note: Note): Boolean + abstract fun moveEffects(from: Int, to: Int) companion object { val instruments = mutableListOf() diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index d24f474..40f3e97 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -78,15 +78,19 @@ ) } - fun applyEffectAttributes(effect: Effect) { + fun applyEffectAttributes(instrument: Instrument, effect: Effect) { applyEffectAttributes( id, - effect.type.ordinal, + instrument.effects.indexOf(effect), if (effect.active) effect.influence.value else 0f, - effect.parameters[0].value + effect.parameters[0]?.value ?: 0f ) } + fun moveEffects(from: Int, to: Int) { + moveEffects(id, from, to) + } + private external fun createInstrument(): Int private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) @@ -107,4 +111,6 @@ influence: Float, parameter1: Float ) + + private external fun moveEffects(id: Int, from: Int, to: 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 e631548..b702455 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -33,14 +33,6 @@ internalInstrument.muted = value } - override fun startNote(note: Note) { - internalInstrument.startNote(note) - } - - override fun stop() { - internalInstrument.endNote() - } - override fun stopNote(note: Note) { if (note == internalInstrument.note) { stop() @@ -51,15 +43,15 @@ internalInstrument.destroy() } - override fun updateEnvelope() { - internalInstrument.applyEnvelope(envelope) - } - override fun updateEffects() { for (effect in effects) { - internalInstrument.applyEffectAttributes(effect) + internalInstrument.applyEffectAttributes(this, effect) } } override fun isPlaying(note: Note): Boolean = internalInstrument.note == note + override fun moveEffects(from: Int, to: Int) = internalInstrument.moveEffects(from, to) + override fun updateEnvelope() = internalInstrument.applyEnvelope(envelope) + override fun startNote(note: Note) = internalInstrument.startNote(note) + override fun stop() = internalInstrument.endNote() } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 7310a37..d8b0f5d 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,8 +9,12 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); - lowPass->host = host; + auto *filter = new LowPass(); + filter->host = host; + effects.push_back(filter); + auto *noise = new Noise(); noise->host = host; + effects.push_back(noise); } void multiply(float *target, float *modulation, uint32_t size) { @@ -44,8 +48,9 @@ void Instrument::render(float *buffer, uint32_t count) { float *waveform = wave->render(count); - processEffect(waveform, count, lowPass); - processEffect(waveform, count, noise); + for (auto effect: effects) { + processEffect(waveform, count, effect); + } multiply(waveform, envelope->render(count), count); multiply(waveform, volume, count); add(buffer, waveform, count); @@ -54,8 +59,10 @@ void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); - lowPass->frequency = frequency; - lowPass->update(); + for (auto effect: effects) { + effect->frequency = frequency; + effect->update(); + } } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 077bfe0..df45330 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -17,8 +17,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; - LowPass *lowPass = new LowPass(); - Noise *noise = new Noise(); + std::list effects; float volume = 0; void render(float *buffer, uint32_t count); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 7ccc60b..2cc9efa 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -101,16 +101,24 @@ jfloat influence, jfloat parameter1) { Instrument *instrument = getInstrument(id); - Effect *effect; - switch (effect_number) { - case 0: - effect = instrument->lowPass; - break; - case 1: - effect = instrument->noise; - break; - } + auto iterator = instrument->effects.begin(); + std::advance(iterator, effect_number); + auto *effect = *iterator; effect->influence = influence; effect->parameter1 = parameter1; } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_moveEffects(JNIEnv *env, jobject thiz, jint id, + jint from, jint to) { + Instrument *instrument = getInstrument(id); + auto source = instrument->effects.begin(); + std::advance(source, from); + auto destination = instrument->effects.begin(); + std::advance(destination, to); + if (instrument->effects.size() == to + 1) { + destination = instrument->effects.end(); + } + instrument->effects.splice(destination, instrument->effects, source); +} } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index b9fa2c1..70d173a 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -18,7 +18,7 @@ abstract class Instrument(var name: String) { var voice: Voice = Voice(this) var envelope = Envelope(this) - val effects = Array(EffectType.VALUES.size) { + val effects = MutableList(EffectType.VALUES.size) { Effect(EffectType.VALUES[it], this) } @@ -33,6 +33,7 @@ abstract fun updateEnvelope() abstract fun updateEffects() abstract fun isPlaying(note: Note): Boolean + abstract fun moveEffects(from: Int, to: Int) companion object { val instruments = mutableListOf() diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index d24f474..40f3e97 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -78,15 +78,19 @@ ) } - fun applyEffectAttributes(effect: Effect) { + fun applyEffectAttributes(instrument: Instrument, effect: Effect) { applyEffectAttributes( id, - effect.type.ordinal, + instrument.effects.indexOf(effect), if (effect.active) effect.influence.value else 0f, - effect.parameters[0].value + effect.parameters[0]?.value ?: 0f ) } + fun moveEffects(from: Int, to: Int) { + moveEffects(id, from, to) + } + private external fun createInstrument(): Int private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) @@ -107,4 +111,6 @@ influence: Float, parameter1: Float ) + + private external fun moveEffects(id: Int, from: Int, to: 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 e631548..b702455 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -33,14 +33,6 @@ internalInstrument.muted = value } - override fun startNote(note: Note) { - internalInstrument.startNote(note) - } - - override fun stop() { - internalInstrument.endNote() - } - override fun stopNote(note: Note) { if (note == internalInstrument.note) { stop() @@ -51,15 +43,15 @@ internalInstrument.destroy() } - override fun updateEnvelope() { - internalInstrument.applyEnvelope(envelope) - } - override fun updateEffects() { for (effect in effects) { - internalInstrument.applyEffectAttributes(effect) + internalInstrument.applyEffectAttributes(this, effect) } } override fun isPlaying(note: Note): Boolean = internalInstrument.note == note + override fun moveEffects(from: Int, to: Int) = internalInstrument.moveEffects(from, to) + override fun updateEnvelope() = internalInstrument.applyEnvelope(envelope) + override fun startNote(note: Note) = internalInstrument.startNote(note) + override fun stop() = internalInstrument.endNote() } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 7beb64c..b33c439 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -86,7 +86,7 @@ override fun updateEffects() { for (instrument in internalInstruments) { for (effect in effects) { - instrument.applyEffectAttributes(effect) + instrument.applyEffectAttributes(this, effect) } } } @@ -99,4 +99,10 @@ } return false } + + override fun moveEffects(from: Int, to: Int) { + for (instrument in internalInstruments) { + instrument.moveEffects(from, to) + } + } } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 7310a37..d8b0f5d 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,8 +9,12 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); - lowPass->host = host; + auto *filter = new LowPass(); + filter->host = host; + effects.push_back(filter); + auto *noise = new Noise(); noise->host = host; + effects.push_back(noise); } void multiply(float *target, float *modulation, uint32_t size) { @@ -44,8 +48,9 @@ void Instrument::render(float *buffer, uint32_t count) { float *waveform = wave->render(count); - processEffect(waveform, count, lowPass); - processEffect(waveform, count, noise); + for (auto effect: effects) { + processEffect(waveform, count, effect); + } multiply(waveform, envelope->render(count), count); multiply(waveform, volume, count); add(buffer, waveform, count); @@ -54,8 +59,10 @@ void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); - lowPass->frequency = frequency; - lowPass->update(); + for (auto effect: effects) { + effect->frequency = frequency; + effect->update(); + } } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 077bfe0..df45330 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -17,8 +17,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; - LowPass *lowPass = new LowPass(); - Noise *noise = new Noise(); + std::list effects; float volume = 0; void render(float *buffer, uint32_t count); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 7ccc60b..2cc9efa 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -101,16 +101,24 @@ jfloat influence, jfloat parameter1) { Instrument *instrument = getInstrument(id); - Effect *effect; - switch (effect_number) { - case 0: - effect = instrument->lowPass; - break; - case 1: - effect = instrument->noise; - break; - } + auto iterator = instrument->effects.begin(); + std::advance(iterator, effect_number); + auto *effect = *iterator; effect->influence = influence; effect->parameter1 = parameter1; } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_moveEffects(JNIEnv *env, jobject thiz, jint id, + jint from, jint to) { + Instrument *instrument = getInstrument(id); + auto source = instrument->effects.begin(); + std::advance(source, from); + auto destination = instrument->effects.begin(); + std::advance(destination, to); + if (instrument->effects.size() == to + 1) { + destination = instrument->effects.end(); + } + instrument->effects.splice(destination, instrument->effects, source); +} } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index b9fa2c1..70d173a 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -18,7 +18,7 @@ abstract class Instrument(var name: String) { var voice: Voice = Voice(this) var envelope = Envelope(this) - val effects = Array(EffectType.VALUES.size) { + val effects = MutableList(EffectType.VALUES.size) { Effect(EffectType.VALUES[it], this) } @@ -33,6 +33,7 @@ abstract fun updateEnvelope() abstract fun updateEffects() abstract fun isPlaying(note: Note): Boolean + abstract fun moveEffects(from: Int, to: Int) companion object { val instruments = mutableListOf() diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index d24f474..40f3e97 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -78,15 +78,19 @@ ) } - fun applyEffectAttributes(effect: Effect) { + fun applyEffectAttributes(instrument: Instrument, effect: Effect) { applyEffectAttributes( id, - effect.type.ordinal, + instrument.effects.indexOf(effect), if (effect.active) effect.influence.value else 0f, - effect.parameters[0].value + effect.parameters[0]?.value ?: 0f ) } + fun moveEffects(from: Int, to: Int) { + moveEffects(id, from, to) + } + private external fun createInstrument(): Int private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) @@ -107,4 +111,6 @@ influence: Float, parameter1: Float ) + + private external fun moveEffects(id: Int, from: Int, to: 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 e631548..b702455 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -33,14 +33,6 @@ internalInstrument.muted = value } - override fun startNote(note: Note) { - internalInstrument.startNote(note) - } - - override fun stop() { - internalInstrument.endNote() - } - override fun stopNote(note: Note) { if (note == internalInstrument.note) { stop() @@ -51,15 +43,15 @@ internalInstrument.destroy() } - override fun updateEnvelope() { - internalInstrument.applyEnvelope(envelope) - } - override fun updateEffects() { for (effect in effects) { - internalInstrument.applyEffectAttributes(effect) + internalInstrument.applyEffectAttributes(this, effect) } } override fun isPlaying(note: Note): Boolean = internalInstrument.note == note + override fun moveEffects(from: Int, to: Int) = internalInstrument.moveEffects(from, to) + override fun updateEnvelope() = internalInstrument.applyEnvelope(envelope) + override fun startNote(note: Note) = internalInstrument.startNote(note) + override fun stop() = internalInstrument.endNote() } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 7beb64c..b33c439 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -86,7 +86,7 @@ override fun updateEffects() { for (instrument in internalInstruments) { for (effect in effects) { - instrument.applyEffectAttributes(effect) + instrument.applyEffectAttributes(this, effect) } } } @@ -99,4 +99,10 @@ } return false } + + override fun moveEffects(from: Int, to: Int) { + for (instrument in internalInstruments) { + instrument.moveEffects(from, to) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt index f921b1b..f659a8c 100644 --- a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt +++ b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt @@ -14,7 +14,9 @@ class Effect(val type: EffectType, private val instrument: Instrument) { val parameters = Array(type.parameterDescriptions.size) { - EffectParameter(type.parameterDescriptions[it], instrument) + type.parameterDescriptions[it]?.let { parameterDescription -> + EffectParameter(parameterDescription, instrument) + } } val influence = EffectParameter(influenceDescription, instrument) diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 7310a37..d8b0f5d 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,8 +9,12 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); - lowPass->host = host; + auto *filter = new LowPass(); + filter->host = host; + effects.push_back(filter); + auto *noise = new Noise(); noise->host = host; + effects.push_back(noise); } void multiply(float *target, float *modulation, uint32_t size) { @@ -44,8 +48,9 @@ void Instrument::render(float *buffer, uint32_t count) { float *waveform = wave->render(count); - processEffect(waveform, count, lowPass); - processEffect(waveform, count, noise); + for (auto effect: effects) { + processEffect(waveform, count, effect); + } multiply(waveform, envelope->render(count), count); multiply(waveform, volume, count); add(buffer, waveform, count); @@ -54,8 +59,10 @@ void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); - lowPass->frequency = frequency; - lowPass->update(); + for (auto effect: effects) { + effect->frequency = frequency; + effect->update(); + } } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 077bfe0..df45330 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -17,8 +17,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; - LowPass *lowPass = new LowPass(); - Noise *noise = new Noise(); + std::list effects; float volume = 0; void render(float *buffer, uint32_t count); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 7ccc60b..2cc9efa 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -101,16 +101,24 @@ jfloat influence, jfloat parameter1) { Instrument *instrument = getInstrument(id); - Effect *effect; - switch (effect_number) { - case 0: - effect = instrument->lowPass; - break; - case 1: - effect = instrument->noise; - break; - } + auto iterator = instrument->effects.begin(); + std::advance(iterator, effect_number); + auto *effect = *iterator; effect->influence = influence; effect->parameter1 = parameter1; } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_moveEffects(JNIEnv *env, jobject thiz, jint id, + jint from, jint to) { + Instrument *instrument = getInstrument(id); + auto source = instrument->effects.begin(); + std::advance(source, from); + auto destination = instrument->effects.begin(); + std::advance(destination, to); + if (instrument->effects.size() == to + 1) { + destination = instrument->effects.end(); + } + instrument->effects.splice(destination, instrument->effects, source); +} } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index b9fa2c1..70d173a 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -18,7 +18,7 @@ abstract class Instrument(var name: String) { var voice: Voice = Voice(this) var envelope = Envelope(this) - val effects = Array(EffectType.VALUES.size) { + val effects = MutableList(EffectType.VALUES.size) { Effect(EffectType.VALUES[it], this) } @@ -33,6 +33,7 @@ abstract fun updateEnvelope() abstract fun updateEffects() abstract fun isPlaying(note: Note): Boolean + abstract fun moveEffects(from: Int, to: Int) companion object { val instruments = mutableListOf() diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index d24f474..40f3e97 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -78,15 +78,19 @@ ) } - fun applyEffectAttributes(effect: Effect) { + fun applyEffectAttributes(instrument: Instrument, effect: Effect) { applyEffectAttributes( id, - effect.type.ordinal, + instrument.effects.indexOf(effect), if (effect.active) effect.influence.value else 0f, - effect.parameters[0].value + effect.parameters[0]?.value ?: 0f ) } + fun moveEffects(from: Int, to: Int) { + moveEffects(id, from, to) + } + private external fun createInstrument(): Int private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) @@ -107,4 +111,6 @@ influence: Float, parameter1: Float ) + + private external fun moveEffects(id: Int, from: Int, to: 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 e631548..b702455 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -33,14 +33,6 @@ internalInstrument.muted = value } - override fun startNote(note: Note) { - internalInstrument.startNote(note) - } - - override fun stop() { - internalInstrument.endNote() - } - override fun stopNote(note: Note) { if (note == internalInstrument.note) { stop() @@ -51,15 +43,15 @@ internalInstrument.destroy() } - override fun updateEnvelope() { - internalInstrument.applyEnvelope(envelope) - } - override fun updateEffects() { for (effect in effects) { - internalInstrument.applyEffectAttributes(effect) + internalInstrument.applyEffectAttributes(this, effect) } } override fun isPlaying(note: Note): Boolean = internalInstrument.note == note + override fun moveEffects(from: Int, to: Int) = internalInstrument.moveEffects(from, to) + override fun updateEnvelope() = internalInstrument.applyEnvelope(envelope) + override fun startNote(note: Note) = internalInstrument.startNote(note) + override fun stop() = internalInstrument.endNote() } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 7beb64c..b33c439 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -86,7 +86,7 @@ override fun updateEffects() { for (instrument in internalInstruments) { for (effect in effects) { - instrument.applyEffectAttributes(effect) + instrument.applyEffectAttributes(this, effect) } } } @@ -99,4 +99,10 @@ } return false } + + override fun moveEffects(from: Int, to: Int) { + for (instrument in internalInstruments) { + instrument.moveEffects(from, to) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt index f921b1b..f659a8c 100644 --- a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt +++ b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt @@ -14,7 +14,9 @@ class Effect(val type: EffectType, private val instrument: Instrument) { val parameters = Array(type.parameterDescriptions.size) { - EffectParameter(type.parameterDescriptions[it], instrument) + type.parameterDescriptions[it]?.let { parameterDescription -> + EffectParameter(parameterDescription, instrument) + } } val influence = EffectParameter(influenceDescription, instrument) diff --git a/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt b/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt index 8af39ea..963d4e3 100644 --- a/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt +++ b/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt @@ -13,8 +13,8 @@ import com.lukas.music.util.format enum class EffectType( - val title: String, - val parameterDescriptions: Array + private val title: String, + val parameterDescriptions: Array ) { LowPass("low pass filter", arrayOf( @@ -24,9 +24,7 @@ )), Noise("noise", arrayOf( - EffectParameterDescription(0f, 1f, 0f) { - "unused" - } + null ) ) ; diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 7310a37..d8b0f5d 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,8 +9,12 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); - lowPass->host = host; + auto *filter = new LowPass(); + filter->host = host; + effects.push_back(filter); + auto *noise = new Noise(); noise->host = host; + effects.push_back(noise); } void multiply(float *target, float *modulation, uint32_t size) { @@ -44,8 +48,9 @@ void Instrument::render(float *buffer, uint32_t count) { float *waveform = wave->render(count); - processEffect(waveform, count, lowPass); - processEffect(waveform, count, noise); + for (auto effect: effects) { + processEffect(waveform, count, effect); + } multiply(waveform, envelope->render(count), count); multiply(waveform, volume, count); add(buffer, waveform, count); @@ -54,8 +59,10 @@ void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); - lowPass->frequency = frequency; - lowPass->update(); + for (auto effect: effects) { + effect->frequency = frequency; + effect->update(); + } } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 077bfe0..df45330 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -17,8 +17,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; - LowPass *lowPass = new LowPass(); - Noise *noise = new Noise(); + std::list effects; float volume = 0; void render(float *buffer, uint32_t count); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 7ccc60b..2cc9efa 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -101,16 +101,24 @@ jfloat influence, jfloat parameter1) { Instrument *instrument = getInstrument(id); - Effect *effect; - switch (effect_number) { - case 0: - effect = instrument->lowPass; - break; - case 1: - effect = instrument->noise; - break; - } + auto iterator = instrument->effects.begin(); + std::advance(iterator, effect_number); + auto *effect = *iterator; effect->influence = influence; effect->parameter1 = parameter1; } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_moveEffects(JNIEnv *env, jobject thiz, jint id, + jint from, jint to) { + Instrument *instrument = getInstrument(id); + auto source = instrument->effects.begin(); + std::advance(source, from); + auto destination = instrument->effects.begin(); + std::advance(destination, to); + if (instrument->effects.size() == to + 1) { + destination = instrument->effects.end(); + } + instrument->effects.splice(destination, instrument->effects, source); +} } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index b9fa2c1..70d173a 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -18,7 +18,7 @@ abstract class Instrument(var name: String) { var voice: Voice = Voice(this) var envelope = Envelope(this) - val effects = Array(EffectType.VALUES.size) { + val effects = MutableList(EffectType.VALUES.size) { Effect(EffectType.VALUES[it], this) } @@ -33,6 +33,7 @@ abstract fun updateEnvelope() abstract fun updateEffects() abstract fun isPlaying(note: Note): Boolean + abstract fun moveEffects(from: Int, to: Int) companion object { val instruments = mutableListOf() diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index d24f474..40f3e97 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -78,15 +78,19 @@ ) } - fun applyEffectAttributes(effect: Effect) { + fun applyEffectAttributes(instrument: Instrument, effect: Effect) { applyEffectAttributes( id, - effect.type.ordinal, + instrument.effects.indexOf(effect), if (effect.active) effect.influence.value else 0f, - effect.parameters[0].value + effect.parameters[0]?.value ?: 0f ) } + fun moveEffects(from: Int, to: Int) { + moveEffects(id, from, to) + } + private external fun createInstrument(): Int private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) @@ -107,4 +111,6 @@ influence: Float, parameter1: Float ) + + private external fun moveEffects(id: Int, from: Int, to: 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 e631548..b702455 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -33,14 +33,6 @@ internalInstrument.muted = value } - override fun startNote(note: Note) { - internalInstrument.startNote(note) - } - - override fun stop() { - internalInstrument.endNote() - } - override fun stopNote(note: Note) { if (note == internalInstrument.note) { stop() @@ -51,15 +43,15 @@ internalInstrument.destroy() } - override fun updateEnvelope() { - internalInstrument.applyEnvelope(envelope) - } - override fun updateEffects() { for (effect in effects) { - internalInstrument.applyEffectAttributes(effect) + internalInstrument.applyEffectAttributes(this, effect) } } override fun isPlaying(note: Note): Boolean = internalInstrument.note == note + override fun moveEffects(from: Int, to: Int) = internalInstrument.moveEffects(from, to) + override fun updateEnvelope() = internalInstrument.applyEnvelope(envelope) + override fun startNote(note: Note) = internalInstrument.startNote(note) + override fun stop() = internalInstrument.endNote() } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 7beb64c..b33c439 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -86,7 +86,7 @@ override fun updateEffects() { for (instrument in internalInstruments) { for (effect in effects) { - instrument.applyEffectAttributes(effect) + instrument.applyEffectAttributes(this, effect) } } } @@ -99,4 +99,10 @@ } return false } + + override fun moveEffects(from: Int, to: Int) { + for (instrument in internalInstruments) { + instrument.moveEffects(from, to) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt index f921b1b..f659a8c 100644 --- a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt +++ b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt @@ -14,7 +14,9 @@ class Effect(val type: EffectType, private val instrument: Instrument) { val parameters = Array(type.parameterDescriptions.size) { - EffectParameter(type.parameterDescriptions[it], instrument) + type.parameterDescriptions[it]?.let { parameterDescription -> + EffectParameter(parameterDescription, instrument) + } } val influence = EffectParameter(influenceDescription, instrument) diff --git a/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt b/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt index 8af39ea..963d4e3 100644 --- a/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt +++ b/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt @@ -13,8 +13,8 @@ import com.lukas.music.util.format enum class EffectType( - val title: String, - val parameterDescriptions: Array + private val title: String, + val parameterDescriptions: Array ) { LowPass("low pass filter", arrayOf( @@ -24,9 +24,7 @@ )), Noise("noise", arrayOf( - EffectParameterDescription(0f, 1f, 0f) { - "unused" - } + null ) ) ; diff --git a/app/src/main/java/com/lukas/music/ui/adapters/EffectsAdapter.kt b/app/src/main/java/com/lukas/music/ui/adapters/EffectsAdapter.kt new file mode 100644 index 0000000..730eabd --- /dev/null +++ b/app/src/main/java/com/lukas/music/ui/adapters/EffectsAdapter.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 Lukas Eisenhauer + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + +package com.lukas.music.ui.adapters + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.lukas.music.databinding.FragmentEffectBinding +import com.lukas.music.instruments.Instrument +import com.lukas.music.ui.fragments.EditEffectsFragment +import com.lukas.music.ui.fragments.EffectFragment + +class EffectsAdapter(private val parent: EditEffectsFragment, private val instrument: Instrument) : + RecyclerView.Adapter() { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EffectFragment { + val context = parent.context + val inflater = LayoutInflater.from(context) + val binding = FragmentEffectBinding.inflate(inflater, parent, false) + return EffectFragment(binding) + } + + override fun onBindViewHolder(holder: EffectFragment, position: Int) { + holder.setEffect(instrument.effects[position]) + } + + override fun getItemCount(): Int { + return instrument.effects.size + } +} \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 7310a37..d8b0f5d 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,8 +9,12 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); - lowPass->host = host; + auto *filter = new LowPass(); + filter->host = host; + effects.push_back(filter); + auto *noise = new Noise(); noise->host = host; + effects.push_back(noise); } void multiply(float *target, float *modulation, uint32_t size) { @@ -44,8 +48,9 @@ void Instrument::render(float *buffer, uint32_t count) { float *waveform = wave->render(count); - processEffect(waveform, count, lowPass); - processEffect(waveform, count, noise); + for (auto effect: effects) { + processEffect(waveform, count, effect); + } multiply(waveform, envelope->render(count), count); multiply(waveform, volume, count); add(buffer, waveform, count); @@ -54,8 +59,10 @@ void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); - lowPass->frequency = frequency; - lowPass->update(); + for (auto effect: effects) { + effect->frequency = frequency; + effect->update(); + } } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 077bfe0..df45330 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -17,8 +17,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; - LowPass *lowPass = new LowPass(); - Noise *noise = new Noise(); + std::list effects; float volume = 0; void render(float *buffer, uint32_t count); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 7ccc60b..2cc9efa 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -101,16 +101,24 @@ jfloat influence, jfloat parameter1) { Instrument *instrument = getInstrument(id); - Effect *effect; - switch (effect_number) { - case 0: - effect = instrument->lowPass; - break; - case 1: - effect = instrument->noise; - break; - } + auto iterator = instrument->effects.begin(); + std::advance(iterator, effect_number); + auto *effect = *iterator; effect->influence = influence; effect->parameter1 = parameter1; } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_moveEffects(JNIEnv *env, jobject thiz, jint id, + jint from, jint to) { + Instrument *instrument = getInstrument(id); + auto source = instrument->effects.begin(); + std::advance(source, from); + auto destination = instrument->effects.begin(); + std::advance(destination, to); + if (instrument->effects.size() == to + 1) { + destination = instrument->effects.end(); + } + instrument->effects.splice(destination, instrument->effects, source); +} } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index b9fa2c1..70d173a 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -18,7 +18,7 @@ abstract class Instrument(var name: String) { var voice: Voice = Voice(this) var envelope = Envelope(this) - val effects = Array(EffectType.VALUES.size) { + val effects = MutableList(EffectType.VALUES.size) { Effect(EffectType.VALUES[it], this) } @@ -33,6 +33,7 @@ abstract fun updateEnvelope() abstract fun updateEffects() abstract fun isPlaying(note: Note): Boolean + abstract fun moveEffects(from: Int, to: Int) companion object { val instruments = mutableListOf() diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index d24f474..40f3e97 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -78,15 +78,19 @@ ) } - fun applyEffectAttributes(effect: Effect) { + fun applyEffectAttributes(instrument: Instrument, effect: Effect) { applyEffectAttributes( id, - effect.type.ordinal, + instrument.effects.indexOf(effect), if (effect.active) effect.influence.value else 0f, - effect.parameters[0].value + effect.parameters[0]?.value ?: 0f ) } + fun moveEffects(from: Int, to: Int) { + moveEffects(id, from, to) + } + private external fun createInstrument(): Int private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) @@ -107,4 +111,6 @@ influence: Float, parameter1: Float ) + + private external fun moveEffects(id: Int, from: Int, to: 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 e631548..b702455 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -33,14 +33,6 @@ internalInstrument.muted = value } - override fun startNote(note: Note) { - internalInstrument.startNote(note) - } - - override fun stop() { - internalInstrument.endNote() - } - override fun stopNote(note: Note) { if (note == internalInstrument.note) { stop() @@ -51,15 +43,15 @@ internalInstrument.destroy() } - override fun updateEnvelope() { - internalInstrument.applyEnvelope(envelope) - } - override fun updateEffects() { for (effect in effects) { - internalInstrument.applyEffectAttributes(effect) + internalInstrument.applyEffectAttributes(this, effect) } } override fun isPlaying(note: Note): Boolean = internalInstrument.note == note + override fun moveEffects(from: Int, to: Int) = internalInstrument.moveEffects(from, to) + override fun updateEnvelope() = internalInstrument.applyEnvelope(envelope) + override fun startNote(note: Note) = internalInstrument.startNote(note) + override fun stop() = internalInstrument.endNote() } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 7beb64c..b33c439 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -86,7 +86,7 @@ override fun updateEffects() { for (instrument in internalInstruments) { for (effect in effects) { - instrument.applyEffectAttributes(effect) + instrument.applyEffectAttributes(this, effect) } } } @@ -99,4 +99,10 @@ } return false } + + override fun moveEffects(from: Int, to: Int) { + for (instrument in internalInstruments) { + instrument.moveEffects(from, to) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt index f921b1b..f659a8c 100644 --- a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt +++ b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt @@ -14,7 +14,9 @@ class Effect(val type: EffectType, private val instrument: Instrument) { val parameters = Array(type.parameterDescriptions.size) { - EffectParameter(type.parameterDescriptions[it], instrument) + type.parameterDescriptions[it]?.let { parameterDescription -> + EffectParameter(parameterDescription, instrument) + } } val influence = EffectParameter(influenceDescription, instrument) diff --git a/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt b/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt index 8af39ea..963d4e3 100644 --- a/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt +++ b/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt @@ -13,8 +13,8 @@ import com.lukas.music.util.format enum class EffectType( - val title: String, - val parameterDescriptions: Array + private val title: String, + val parameterDescriptions: Array ) { LowPass("low pass filter", arrayOf( @@ -24,9 +24,7 @@ )), Noise("noise", arrayOf( - EffectParameterDescription(0f, 1f, 0f) { - "unused" - } + null ) ) ; diff --git a/app/src/main/java/com/lukas/music/ui/adapters/EffectsAdapter.kt b/app/src/main/java/com/lukas/music/ui/adapters/EffectsAdapter.kt new file mode 100644 index 0000000..730eabd --- /dev/null +++ b/app/src/main/java/com/lukas/music/ui/adapters/EffectsAdapter.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 Lukas Eisenhauer + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + +package com.lukas.music.ui.adapters + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.lukas.music.databinding.FragmentEffectBinding +import com.lukas.music.instruments.Instrument +import com.lukas.music.ui.fragments.EditEffectsFragment +import com.lukas.music.ui.fragments.EffectFragment + +class EffectsAdapter(private val parent: EditEffectsFragment, private val instrument: Instrument) : + RecyclerView.Adapter() { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EffectFragment { + val context = parent.context + val inflater = LayoutInflater.from(context) + val binding = FragmentEffectBinding.inflate(inflater, parent, false) + return EffectFragment(binding) + } + + override fun onBindViewHolder(holder: EffectFragment, position: Int) { + holder.setEffect(instrument.effects[position]) + } + + override fun getItemCount(): Int { + return instrument.effects.size + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/ui/fragments/EditEffectsFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/EditEffectsFragment.kt index 6e359e4..0d532c2 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/EditEffectsFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/EditEffectsFragment.kt @@ -14,9 +14,13 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.LinearLayoutManager import com.lukas.music.databinding.FragmentEditEffectsBinding import com.lukas.music.instruments.Instrument +import com.lukas.music.ui.adapters.EffectsAdapter import com.lukas.music.util.EasyDialogFragment +import com.lukas.music.util.makeMoveCallback class EditEffectsFragment(private val instrument: Instrument) : EasyDialogFragment() { @@ -25,11 +29,12 @@ savedInstanceState: Bundle? ): View? { binding = FragmentEditEffectsBinding.inflate(inflater) - for (effect in instrument.effects) { - val effectEditor = EffectFragment(effect) - childFragmentManager.beginTransaction().add(binding.effectsDisplay.id, effectEditor) - .commit() - } + binding.effectsDisplay.adapter = EffectsAdapter(this, instrument) + binding.effectsDisplay.layoutManager = LinearLayoutManager(context) + val helper = ItemTouchHelper(makeMoveCallback(instrument.effects) { from, to -> + instrument.moveEffects(from, to) + }) + helper.attachToRecyclerView(binding.effectsDisplay) binding.closeButton.setOnClickListener { dismiss() } diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 7310a37..d8b0f5d 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,8 +9,12 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); - lowPass->host = host; + auto *filter = new LowPass(); + filter->host = host; + effects.push_back(filter); + auto *noise = new Noise(); noise->host = host; + effects.push_back(noise); } void multiply(float *target, float *modulation, uint32_t size) { @@ -44,8 +48,9 @@ void Instrument::render(float *buffer, uint32_t count) { float *waveform = wave->render(count); - processEffect(waveform, count, lowPass); - processEffect(waveform, count, noise); + for (auto effect: effects) { + processEffect(waveform, count, effect); + } multiply(waveform, envelope->render(count), count); multiply(waveform, volume, count); add(buffer, waveform, count); @@ -54,8 +59,10 @@ void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); - lowPass->frequency = frequency; - lowPass->update(); + for (auto effect: effects) { + effect->frequency = frequency; + effect->update(); + } } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 077bfe0..df45330 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -17,8 +17,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; - LowPass *lowPass = new LowPass(); - Noise *noise = new Noise(); + std::list effects; float volume = 0; void render(float *buffer, uint32_t count); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 7ccc60b..2cc9efa 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -101,16 +101,24 @@ jfloat influence, jfloat parameter1) { Instrument *instrument = getInstrument(id); - Effect *effect; - switch (effect_number) { - case 0: - effect = instrument->lowPass; - break; - case 1: - effect = instrument->noise; - break; - } + auto iterator = instrument->effects.begin(); + std::advance(iterator, effect_number); + auto *effect = *iterator; effect->influence = influence; effect->parameter1 = parameter1; } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_moveEffects(JNIEnv *env, jobject thiz, jint id, + jint from, jint to) { + Instrument *instrument = getInstrument(id); + auto source = instrument->effects.begin(); + std::advance(source, from); + auto destination = instrument->effects.begin(); + std::advance(destination, to); + if (instrument->effects.size() == to + 1) { + destination = instrument->effects.end(); + } + instrument->effects.splice(destination, instrument->effects, source); +} } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index b9fa2c1..70d173a 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -18,7 +18,7 @@ abstract class Instrument(var name: String) { var voice: Voice = Voice(this) var envelope = Envelope(this) - val effects = Array(EffectType.VALUES.size) { + val effects = MutableList(EffectType.VALUES.size) { Effect(EffectType.VALUES[it], this) } @@ -33,6 +33,7 @@ abstract fun updateEnvelope() abstract fun updateEffects() abstract fun isPlaying(note: Note): Boolean + abstract fun moveEffects(from: Int, to: Int) companion object { val instruments = mutableListOf() diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index d24f474..40f3e97 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -78,15 +78,19 @@ ) } - fun applyEffectAttributes(effect: Effect) { + fun applyEffectAttributes(instrument: Instrument, effect: Effect) { applyEffectAttributes( id, - effect.type.ordinal, + instrument.effects.indexOf(effect), if (effect.active) effect.influence.value else 0f, - effect.parameters[0].value + effect.parameters[0]?.value ?: 0f ) } + fun moveEffects(from: Int, to: Int) { + moveEffects(id, from, to) + } + private external fun createInstrument(): Int private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) @@ -107,4 +111,6 @@ influence: Float, parameter1: Float ) + + private external fun moveEffects(id: Int, from: Int, to: 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 e631548..b702455 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -33,14 +33,6 @@ internalInstrument.muted = value } - override fun startNote(note: Note) { - internalInstrument.startNote(note) - } - - override fun stop() { - internalInstrument.endNote() - } - override fun stopNote(note: Note) { if (note == internalInstrument.note) { stop() @@ -51,15 +43,15 @@ internalInstrument.destroy() } - override fun updateEnvelope() { - internalInstrument.applyEnvelope(envelope) - } - override fun updateEffects() { for (effect in effects) { - internalInstrument.applyEffectAttributes(effect) + internalInstrument.applyEffectAttributes(this, effect) } } override fun isPlaying(note: Note): Boolean = internalInstrument.note == note + override fun moveEffects(from: Int, to: Int) = internalInstrument.moveEffects(from, to) + override fun updateEnvelope() = internalInstrument.applyEnvelope(envelope) + override fun startNote(note: Note) = internalInstrument.startNote(note) + override fun stop() = internalInstrument.endNote() } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 7beb64c..b33c439 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -86,7 +86,7 @@ override fun updateEffects() { for (instrument in internalInstruments) { for (effect in effects) { - instrument.applyEffectAttributes(effect) + instrument.applyEffectAttributes(this, effect) } } } @@ -99,4 +99,10 @@ } return false } + + override fun moveEffects(from: Int, to: Int) { + for (instrument in internalInstruments) { + instrument.moveEffects(from, to) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt index f921b1b..f659a8c 100644 --- a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt +++ b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt @@ -14,7 +14,9 @@ class Effect(val type: EffectType, private val instrument: Instrument) { val parameters = Array(type.parameterDescriptions.size) { - EffectParameter(type.parameterDescriptions[it], instrument) + type.parameterDescriptions[it]?.let { parameterDescription -> + EffectParameter(parameterDescription, instrument) + } } val influence = EffectParameter(influenceDescription, instrument) diff --git a/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt b/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt index 8af39ea..963d4e3 100644 --- a/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt +++ b/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt @@ -13,8 +13,8 @@ import com.lukas.music.util.format enum class EffectType( - val title: String, - val parameterDescriptions: Array + private val title: String, + val parameterDescriptions: Array ) { LowPass("low pass filter", arrayOf( @@ -24,9 +24,7 @@ )), Noise("noise", arrayOf( - EffectParameterDescription(0f, 1f, 0f) { - "unused" - } + null ) ) ; diff --git a/app/src/main/java/com/lukas/music/ui/adapters/EffectsAdapter.kt b/app/src/main/java/com/lukas/music/ui/adapters/EffectsAdapter.kt new file mode 100644 index 0000000..730eabd --- /dev/null +++ b/app/src/main/java/com/lukas/music/ui/adapters/EffectsAdapter.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 Lukas Eisenhauer + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + +package com.lukas.music.ui.adapters + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.lukas.music.databinding.FragmentEffectBinding +import com.lukas.music.instruments.Instrument +import com.lukas.music.ui.fragments.EditEffectsFragment +import com.lukas.music.ui.fragments.EffectFragment + +class EffectsAdapter(private val parent: EditEffectsFragment, private val instrument: Instrument) : + RecyclerView.Adapter() { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EffectFragment { + val context = parent.context + val inflater = LayoutInflater.from(context) + val binding = FragmentEffectBinding.inflate(inflater, parent, false) + return EffectFragment(binding) + } + + override fun onBindViewHolder(holder: EffectFragment, position: Int) { + holder.setEffect(instrument.effects[position]) + } + + override fun getItemCount(): Int { + return instrument.effects.size + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/ui/fragments/EditEffectsFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/EditEffectsFragment.kt index 6e359e4..0d532c2 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/EditEffectsFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/EditEffectsFragment.kt @@ -14,9 +14,13 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.LinearLayoutManager import com.lukas.music.databinding.FragmentEditEffectsBinding import com.lukas.music.instruments.Instrument +import com.lukas.music.ui.adapters.EffectsAdapter import com.lukas.music.util.EasyDialogFragment +import com.lukas.music.util.makeMoveCallback class EditEffectsFragment(private val instrument: Instrument) : EasyDialogFragment() { @@ -25,11 +29,12 @@ savedInstanceState: Bundle? ): View? { binding = FragmentEditEffectsBinding.inflate(inflater) - for (effect in instrument.effects) { - val effectEditor = EffectFragment(effect) - childFragmentManager.beginTransaction().add(binding.effectsDisplay.id, effectEditor) - .commit() - } + binding.effectsDisplay.adapter = EffectsAdapter(this, instrument) + binding.effectsDisplay.layoutManager = LinearLayoutManager(context) + val helper = ItemTouchHelper(makeMoveCallback(instrument.effects) { from, to -> + instrument.moveEffects(from, to) + }) + helper.attachToRecyclerView(binding.effectsDisplay) binding.closeButton.setOnClickListener { dismiss() } diff --git a/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt index ebf4cb1..7b13ed6 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt @@ -10,25 +10,18 @@ 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 androidx.recyclerview.widget.RecyclerView import com.lukas.music.R import com.lukas.music.databinding.FragmentEffectBinding import com.lukas.music.instruments.effect.Effect import com.lukas.music.util.setupToggle import com.lukas.music.util.smartSetup -class EffectFragment(private val effect: Effect) : Fragment() { - lateinit var binding: FragmentEffectBinding - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - binding = FragmentEffectBinding.inflate(inflater) +class EffectFragment(val binding: FragmentEffectBinding) : RecyclerView.ViewHolder( + binding.root +) { + fun setEffect(effect: Effect) { binding.effectName.text = effect.type.toString() binding.activeButton.setupToggle(effect::active, R.color.blue) { binding.activeButton.text = if (it) "ON" else "OFF" @@ -37,10 +30,15 @@ binding.influenceSeekBar.smartSetup(0, 100, effect.influence::percentageValue) { binding.influenceText.text = effect.influence.description.text(effect.influence) } - binding.parameter1SeekBar.smartSetup(0, 100, effect.parameters[0]::percentageValue) { - binding.parameter1Text.text = - effect.parameters[0].description.text(effect.parameters[0]) + binding.parameter1SeekBar.visibility = + if (effect.parameters[0] == null) View.GONE else View.VISIBLE + binding.parameter1Text.visibility = + if (effect.parameters[0] == null) View.GONE else View.VISIBLE + effect.parameters[0]?.let { + binding.parameter1SeekBar.smartSetup(0, 100, it::percentageValue) { + binding.parameter1Text.text = + effect.parameters[0]!!.description.text(effect.parameters[0]!!) + } } - return binding.root } } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 7310a37..d8b0f5d 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,8 +9,12 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); - lowPass->host = host; + auto *filter = new LowPass(); + filter->host = host; + effects.push_back(filter); + auto *noise = new Noise(); noise->host = host; + effects.push_back(noise); } void multiply(float *target, float *modulation, uint32_t size) { @@ -44,8 +48,9 @@ void Instrument::render(float *buffer, uint32_t count) { float *waveform = wave->render(count); - processEffect(waveform, count, lowPass); - processEffect(waveform, count, noise); + for (auto effect: effects) { + processEffect(waveform, count, effect); + } multiply(waveform, envelope->render(count), count); multiply(waveform, volume, count); add(buffer, waveform, count); @@ -54,8 +59,10 @@ void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); - lowPass->frequency = frequency; - lowPass->update(); + for (auto effect: effects) { + effect->frequency = frequency; + effect->update(); + } } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 077bfe0..df45330 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -17,8 +17,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; - LowPass *lowPass = new LowPass(); - Noise *noise = new Noise(); + std::list effects; float volume = 0; void render(float *buffer, uint32_t count); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 7ccc60b..2cc9efa 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -101,16 +101,24 @@ jfloat influence, jfloat parameter1) { Instrument *instrument = getInstrument(id); - Effect *effect; - switch (effect_number) { - case 0: - effect = instrument->lowPass; - break; - case 1: - effect = instrument->noise; - break; - } + auto iterator = instrument->effects.begin(); + std::advance(iterator, effect_number); + auto *effect = *iterator; effect->influence = influence; effect->parameter1 = parameter1; } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_moveEffects(JNIEnv *env, jobject thiz, jint id, + jint from, jint to) { + Instrument *instrument = getInstrument(id); + auto source = instrument->effects.begin(); + std::advance(source, from); + auto destination = instrument->effects.begin(); + std::advance(destination, to); + if (instrument->effects.size() == to + 1) { + destination = instrument->effects.end(); + } + instrument->effects.splice(destination, instrument->effects, source); +} } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index b9fa2c1..70d173a 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -18,7 +18,7 @@ abstract class Instrument(var name: String) { var voice: Voice = Voice(this) var envelope = Envelope(this) - val effects = Array(EffectType.VALUES.size) { + val effects = MutableList(EffectType.VALUES.size) { Effect(EffectType.VALUES[it], this) } @@ -33,6 +33,7 @@ abstract fun updateEnvelope() abstract fun updateEffects() abstract fun isPlaying(note: Note): Boolean + abstract fun moveEffects(from: Int, to: Int) companion object { val instruments = mutableListOf() diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index d24f474..40f3e97 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -78,15 +78,19 @@ ) } - fun applyEffectAttributes(effect: Effect) { + fun applyEffectAttributes(instrument: Instrument, effect: Effect) { applyEffectAttributes( id, - effect.type.ordinal, + instrument.effects.indexOf(effect), if (effect.active) effect.influence.value else 0f, - effect.parameters[0].value + effect.parameters[0]?.value ?: 0f ) } + fun moveEffects(from: Int, to: Int) { + moveEffects(id, from, to) + } + private external fun createInstrument(): Int private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) @@ -107,4 +111,6 @@ influence: Float, parameter1: Float ) + + private external fun moveEffects(id: Int, from: Int, to: 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 e631548..b702455 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -33,14 +33,6 @@ internalInstrument.muted = value } - override fun startNote(note: Note) { - internalInstrument.startNote(note) - } - - override fun stop() { - internalInstrument.endNote() - } - override fun stopNote(note: Note) { if (note == internalInstrument.note) { stop() @@ -51,15 +43,15 @@ internalInstrument.destroy() } - override fun updateEnvelope() { - internalInstrument.applyEnvelope(envelope) - } - override fun updateEffects() { for (effect in effects) { - internalInstrument.applyEffectAttributes(effect) + internalInstrument.applyEffectAttributes(this, effect) } } override fun isPlaying(note: Note): Boolean = internalInstrument.note == note + override fun moveEffects(from: Int, to: Int) = internalInstrument.moveEffects(from, to) + override fun updateEnvelope() = internalInstrument.applyEnvelope(envelope) + override fun startNote(note: Note) = internalInstrument.startNote(note) + override fun stop() = internalInstrument.endNote() } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 7beb64c..b33c439 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -86,7 +86,7 @@ override fun updateEffects() { for (instrument in internalInstruments) { for (effect in effects) { - instrument.applyEffectAttributes(effect) + instrument.applyEffectAttributes(this, effect) } } } @@ -99,4 +99,10 @@ } return false } + + override fun moveEffects(from: Int, to: Int) { + for (instrument in internalInstruments) { + instrument.moveEffects(from, to) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt index f921b1b..f659a8c 100644 --- a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt +++ b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt @@ -14,7 +14,9 @@ class Effect(val type: EffectType, private val instrument: Instrument) { val parameters = Array(type.parameterDescriptions.size) { - EffectParameter(type.parameterDescriptions[it], instrument) + type.parameterDescriptions[it]?.let { parameterDescription -> + EffectParameter(parameterDescription, instrument) + } } val influence = EffectParameter(influenceDescription, instrument) diff --git a/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt b/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt index 8af39ea..963d4e3 100644 --- a/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt +++ b/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt @@ -13,8 +13,8 @@ import com.lukas.music.util.format enum class EffectType( - val title: String, - val parameterDescriptions: Array + private val title: String, + val parameterDescriptions: Array ) { LowPass("low pass filter", arrayOf( @@ -24,9 +24,7 @@ )), Noise("noise", arrayOf( - EffectParameterDescription(0f, 1f, 0f) { - "unused" - } + null ) ) ; diff --git a/app/src/main/java/com/lukas/music/ui/adapters/EffectsAdapter.kt b/app/src/main/java/com/lukas/music/ui/adapters/EffectsAdapter.kt new file mode 100644 index 0000000..730eabd --- /dev/null +++ b/app/src/main/java/com/lukas/music/ui/adapters/EffectsAdapter.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 Lukas Eisenhauer + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + +package com.lukas.music.ui.adapters + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.lukas.music.databinding.FragmentEffectBinding +import com.lukas.music.instruments.Instrument +import com.lukas.music.ui.fragments.EditEffectsFragment +import com.lukas.music.ui.fragments.EffectFragment + +class EffectsAdapter(private val parent: EditEffectsFragment, private val instrument: Instrument) : + RecyclerView.Adapter() { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EffectFragment { + val context = parent.context + val inflater = LayoutInflater.from(context) + val binding = FragmentEffectBinding.inflate(inflater, parent, false) + return EffectFragment(binding) + } + + override fun onBindViewHolder(holder: EffectFragment, position: Int) { + holder.setEffect(instrument.effects[position]) + } + + override fun getItemCount(): Int { + return instrument.effects.size + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/ui/fragments/EditEffectsFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/EditEffectsFragment.kt index 6e359e4..0d532c2 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/EditEffectsFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/EditEffectsFragment.kt @@ -14,9 +14,13 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.LinearLayoutManager import com.lukas.music.databinding.FragmentEditEffectsBinding import com.lukas.music.instruments.Instrument +import com.lukas.music.ui.adapters.EffectsAdapter import com.lukas.music.util.EasyDialogFragment +import com.lukas.music.util.makeMoveCallback class EditEffectsFragment(private val instrument: Instrument) : EasyDialogFragment() { @@ -25,11 +29,12 @@ savedInstanceState: Bundle? ): View? { binding = FragmentEditEffectsBinding.inflate(inflater) - for (effect in instrument.effects) { - val effectEditor = EffectFragment(effect) - childFragmentManager.beginTransaction().add(binding.effectsDisplay.id, effectEditor) - .commit() - } + binding.effectsDisplay.adapter = EffectsAdapter(this, instrument) + binding.effectsDisplay.layoutManager = LinearLayoutManager(context) + val helper = ItemTouchHelper(makeMoveCallback(instrument.effects) { from, to -> + instrument.moveEffects(from, to) + }) + helper.attachToRecyclerView(binding.effectsDisplay) binding.closeButton.setOnClickListener { dismiss() } diff --git a/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt index ebf4cb1..7b13ed6 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt @@ -10,25 +10,18 @@ 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 androidx.recyclerview.widget.RecyclerView import com.lukas.music.R import com.lukas.music.databinding.FragmentEffectBinding import com.lukas.music.instruments.effect.Effect import com.lukas.music.util.setupToggle import com.lukas.music.util.smartSetup -class EffectFragment(private val effect: Effect) : Fragment() { - lateinit var binding: FragmentEffectBinding - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - binding = FragmentEffectBinding.inflate(inflater) +class EffectFragment(val binding: FragmentEffectBinding) : RecyclerView.ViewHolder( + binding.root +) { + fun setEffect(effect: Effect) { binding.effectName.text = effect.type.toString() binding.activeButton.setupToggle(effect::active, R.color.blue) { binding.activeButton.text = if (it) "ON" else "OFF" @@ -37,10 +30,15 @@ binding.influenceSeekBar.smartSetup(0, 100, effect.influence::percentageValue) { binding.influenceText.text = effect.influence.description.text(effect.influence) } - binding.parameter1SeekBar.smartSetup(0, 100, effect.parameters[0]::percentageValue) { - binding.parameter1Text.text = - effect.parameters[0].description.text(effect.parameters[0]) + binding.parameter1SeekBar.visibility = + if (effect.parameters[0] == null) View.GONE else View.VISIBLE + binding.parameter1Text.visibility = + if (effect.parameters[0] == null) View.GONE else View.VISIBLE + effect.parameters[0]?.let { + binding.parameter1SeekBar.smartSetup(0, 100, it::percentageValue) { + binding.parameter1Text.text = + effect.parameters[0]!!.description.text(effect.parameters[0]!!) + } } - return binding.root } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/ui/fragments/InstrumentListFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/InstrumentListFragment.kt index 1d35c10..0b66892 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/InstrumentListFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/InstrumentListFragment.kt @@ -24,6 +24,7 @@ import com.lukas.music.instruments.MonoInstrument import com.lukas.music.instruments.PolyInstrument import com.lukas.music.ui.adapters.InstrumentAdapter +import com.lukas.music.util.makeMoveCallback class InstrumentListFragment : Fragment() { lateinit var binding: FragmentInstrumentListBinding @@ -35,32 +36,7 @@ binding = FragmentInstrumentListBinding.inflate(inflater) binding.recyclerView.adapter = InstrumentAdapter(this) binding.recyclerView.layoutManager = LinearLayoutManager(context) - val callback = object : ItemTouchHelper.SimpleCallback( - ItemTouchHelper.UP or ItemTouchHelper.DOWN, - 0 - ) { - override fun onMove( - recyclerView: RecyclerView, - viewHolder: RecyclerView.ViewHolder, - target: RecyclerView.ViewHolder - ): Boolean { - val adapter = recyclerView.adapter as InstrumentAdapter - val startPosition = viewHolder.adapterPosition - val endPosition = target.adapterPosition - val instrument = Instrument.instruments[startPosition] - Instrument.instruments.removeAt(startPosition) - if (endPosition < startPosition) { - Instrument.instruments.add(endPosition + 1, instrument) - } else { - Instrument.instruments.add(endPosition - 1, instrument) - } - adapter.notifyItemMoved(startPosition, endPosition) - return true - } - - override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {} - } - val helper = ItemTouchHelper(callback) + val helper = ItemTouchHelper(makeMoveCallback(Instrument.instruments)) helper.attachToRecyclerView(binding.recyclerView) val builder = AlertDialog.Builder(binding.root.context) diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 7310a37..d8b0f5d 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,8 +9,12 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); - lowPass->host = host; + auto *filter = new LowPass(); + filter->host = host; + effects.push_back(filter); + auto *noise = new Noise(); noise->host = host; + effects.push_back(noise); } void multiply(float *target, float *modulation, uint32_t size) { @@ -44,8 +48,9 @@ void Instrument::render(float *buffer, uint32_t count) { float *waveform = wave->render(count); - processEffect(waveform, count, lowPass); - processEffect(waveform, count, noise); + for (auto effect: effects) { + processEffect(waveform, count, effect); + } multiply(waveform, envelope->render(count), count); multiply(waveform, volume, count); add(buffer, waveform, count); @@ -54,8 +59,10 @@ void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); - lowPass->frequency = frequency; - lowPass->update(); + for (auto effect: effects) { + effect->frequency = frequency; + effect->update(); + } } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 077bfe0..df45330 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -17,8 +17,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; - LowPass *lowPass = new LowPass(); - Noise *noise = new Noise(); + std::list effects; float volume = 0; void render(float *buffer, uint32_t count); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 7ccc60b..2cc9efa 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -101,16 +101,24 @@ jfloat influence, jfloat parameter1) { Instrument *instrument = getInstrument(id); - Effect *effect; - switch (effect_number) { - case 0: - effect = instrument->lowPass; - break; - case 1: - effect = instrument->noise; - break; - } + auto iterator = instrument->effects.begin(); + std::advance(iterator, effect_number); + auto *effect = *iterator; effect->influence = influence; effect->parameter1 = parameter1; } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_moveEffects(JNIEnv *env, jobject thiz, jint id, + jint from, jint to) { + Instrument *instrument = getInstrument(id); + auto source = instrument->effects.begin(); + std::advance(source, from); + auto destination = instrument->effects.begin(); + std::advance(destination, to); + if (instrument->effects.size() == to + 1) { + destination = instrument->effects.end(); + } + instrument->effects.splice(destination, instrument->effects, source); +} } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index b9fa2c1..70d173a 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -18,7 +18,7 @@ abstract class Instrument(var name: String) { var voice: Voice = Voice(this) var envelope = Envelope(this) - val effects = Array(EffectType.VALUES.size) { + val effects = MutableList(EffectType.VALUES.size) { Effect(EffectType.VALUES[it], this) } @@ -33,6 +33,7 @@ abstract fun updateEnvelope() abstract fun updateEffects() abstract fun isPlaying(note: Note): Boolean + abstract fun moveEffects(from: Int, to: Int) companion object { val instruments = mutableListOf() diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index d24f474..40f3e97 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -78,15 +78,19 @@ ) } - fun applyEffectAttributes(effect: Effect) { + fun applyEffectAttributes(instrument: Instrument, effect: Effect) { applyEffectAttributes( id, - effect.type.ordinal, + instrument.effects.indexOf(effect), if (effect.active) effect.influence.value else 0f, - effect.parameters[0].value + effect.parameters[0]?.value ?: 0f ) } + fun moveEffects(from: Int, to: Int) { + moveEffects(id, from, to) + } + private external fun createInstrument(): Int private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) @@ -107,4 +111,6 @@ influence: Float, parameter1: Float ) + + private external fun moveEffects(id: Int, from: Int, to: 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 e631548..b702455 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -33,14 +33,6 @@ internalInstrument.muted = value } - override fun startNote(note: Note) { - internalInstrument.startNote(note) - } - - override fun stop() { - internalInstrument.endNote() - } - override fun stopNote(note: Note) { if (note == internalInstrument.note) { stop() @@ -51,15 +43,15 @@ internalInstrument.destroy() } - override fun updateEnvelope() { - internalInstrument.applyEnvelope(envelope) - } - override fun updateEffects() { for (effect in effects) { - internalInstrument.applyEffectAttributes(effect) + internalInstrument.applyEffectAttributes(this, effect) } } override fun isPlaying(note: Note): Boolean = internalInstrument.note == note + override fun moveEffects(from: Int, to: Int) = internalInstrument.moveEffects(from, to) + override fun updateEnvelope() = internalInstrument.applyEnvelope(envelope) + override fun startNote(note: Note) = internalInstrument.startNote(note) + override fun stop() = internalInstrument.endNote() } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 7beb64c..b33c439 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -86,7 +86,7 @@ override fun updateEffects() { for (instrument in internalInstruments) { for (effect in effects) { - instrument.applyEffectAttributes(effect) + instrument.applyEffectAttributes(this, effect) } } } @@ -99,4 +99,10 @@ } return false } + + override fun moveEffects(from: Int, to: Int) { + for (instrument in internalInstruments) { + instrument.moveEffects(from, to) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt index f921b1b..f659a8c 100644 --- a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt +++ b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt @@ -14,7 +14,9 @@ class Effect(val type: EffectType, private val instrument: Instrument) { val parameters = Array(type.parameterDescriptions.size) { - EffectParameter(type.parameterDescriptions[it], instrument) + type.parameterDescriptions[it]?.let { parameterDescription -> + EffectParameter(parameterDescription, instrument) + } } val influence = EffectParameter(influenceDescription, instrument) diff --git a/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt b/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt index 8af39ea..963d4e3 100644 --- a/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt +++ b/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt @@ -13,8 +13,8 @@ import com.lukas.music.util.format enum class EffectType( - val title: String, - val parameterDescriptions: Array + private val title: String, + val parameterDescriptions: Array ) { LowPass("low pass filter", arrayOf( @@ -24,9 +24,7 @@ )), Noise("noise", arrayOf( - EffectParameterDescription(0f, 1f, 0f) { - "unused" - } + null ) ) ; diff --git a/app/src/main/java/com/lukas/music/ui/adapters/EffectsAdapter.kt b/app/src/main/java/com/lukas/music/ui/adapters/EffectsAdapter.kt new file mode 100644 index 0000000..730eabd --- /dev/null +++ b/app/src/main/java/com/lukas/music/ui/adapters/EffectsAdapter.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 Lukas Eisenhauer + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + +package com.lukas.music.ui.adapters + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.lukas.music.databinding.FragmentEffectBinding +import com.lukas.music.instruments.Instrument +import com.lukas.music.ui.fragments.EditEffectsFragment +import com.lukas.music.ui.fragments.EffectFragment + +class EffectsAdapter(private val parent: EditEffectsFragment, private val instrument: Instrument) : + RecyclerView.Adapter() { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EffectFragment { + val context = parent.context + val inflater = LayoutInflater.from(context) + val binding = FragmentEffectBinding.inflate(inflater, parent, false) + return EffectFragment(binding) + } + + override fun onBindViewHolder(holder: EffectFragment, position: Int) { + holder.setEffect(instrument.effects[position]) + } + + override fun getItemCount(): Int { + return instrument.effects.size + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/ui/fragments/EditEffectsFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/EditEffectsFragment.kt index 6e359e4..0d532c2 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/EditEffectsFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/EditEffectsFragment.kt @@ -14,9 +14,13 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.LinearLayoutManager import com.lukas.music.databinding.FragmentEditEffectsBinding import com.lukas.music.instruments.Instrument +import com.lukas.music.ui.adapters.EffectsAdapter import com.lukas.music.util.EasyDialogFragment +import com.lukas.music.util.makeMoveCallback class EditEffectsFragment(private val instrument: Instrument) : EasyDialogFragment() { @@ -25,11 +29,12 @@ savedInstanceState: Bundle? ): View? { binding = FragmentEditEffectsBinding.inflate(inflater) - for (effect in instrument.effects) { - val effectEditor = EffectFragment(effect) - childFragmentManager.beginTransaction().add(binding.effectsDisplay.id, effectEditor) - .commit() - } + binding.effectsDisplay.adapter = EffectsAdapter(this, instrument) + binding.effectsDisplay.layoutManager = LinearLayoutManager(context) + val helper = ItemTouchHelper(makeMoveCallback(instrument.effects) { from, to -> + instrument.moveEffects(from, to) + }) + helper.attachToRecyclerView(binding.effectsDisplay) binding.closeButton.setOnClickListener { dismiss() } diff --git a/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt index ebf4cb1..7b13ed6 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt @@ -10,25 +10,18 @@ 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 androidx.recyclerview.widget.RecyclerView import com.lukas.music.R import com.lukas.music.databinding.FragmentEffectBinding import com.lukas.music.instruments.effect.Effect import com.lukas.music.util.setupToggle import com.lukas.music.util.smartSetup -class EffectFragment(private val effect: Effect) : Fragment() { - lateinit var binding: FragmentEffectBinding - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - binding = FragmentEffectBinding.inflate(inflater) +class EffectFragment(val binding: FragmentEffectBinding) : RecyclerView.ViewHolder( + binding.root +) { + fun setEffect(effect: Effect) { binding.effectName.text = effect.type.toString() binding.activeButton.setupToggle(effect::active, R.color.blue) { binding.activeButton.text = if (it) "ON" else "OFF" @@ -37,10 +30,15 @@ binding.influenceSeekBar.smartSetup(0, 100, effect.influence::percentageValue) { binding.influenceText.text = effect.influence.description.text(effect.influence) } - binding.parameter1SeekBar.smartSetup(0, 100, effect.parameters[0]::percentageValue) { - binding.parameter1Text.text = - effect.parameters[0].description.text(effect.parameters[0]) + binding.parameter1SeekBar.visibility = + if (effect.parameters[0] == null) View.GONE else View.VISIBLE + binding.parameter1Text.visibility = + if (effect.parameters[0] == null) View.GONE else View.VISIBLE + effect.parameters[0]?.let { + binding.parameter1SeekBar.smartSetup(0, 100, it::percentageValue) { + binding.parameter1Text.text = + effect.parameters[0]!!.description.text(effect.parameters[0]!!) + } } - return binding.root } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/ui/fragments/InstrumentListFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/InstrumentListFragment.kt index 1d35c10..0b66892 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/InstrumentListFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/InstrumentListFragment.kt @@ -24,6 +24,7 @@ import com.lukas.music.instruments.MonoInstrument import com.lukas.music.instruments.PolyInstrument import com.lukas.music.ui.adapters.InstrumentAdapter +import com.lukas.music.util.makeMoveCallback class InstrumentListFragment : Fragment() { lateinit var binding: FragmentInstrumentListBinding @@ -35,32 +36,7 @@ binding = FragmentInstrumentListBinding.inflate(inflater) binding.recyclerView.adapter = InstrumentAdapter(this) binding.recyclerView.layoutManager = LinearLayoutManager(context) - val callback = object : ItemTouchHelper.SimpleCallback( - ItemTouchHelper.UP or ItemTouchHelper.DOWN, - 0 - ) { - override fun onMove( - recyclerView: RecyclerView, - viewHolder: RecyclerView.ViewHolder, - target: RecyclerView.ViewHolder - ): Boolean { - val adapter = recyclerView.adapter as InstrumentAdapter - val startPosition = viewHolder.adapterPosition - val endPosition = target.adapterPosition - val instrument = Instrument.instruments[startPosition] - Instrument.instruments.removeAt(startPosition) - if (endPosition < startPosition) { - Instrument.instruments.add(endPosition + 1, instrument) - } else { - Instrument.instruments.add(endPosition - 1, instrument) - } - adapter.notifyItemMoved(startPosition, endPosition) - return true - } - - override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {} - } - val helper = ItemTouchHelper(callback) + val helper = ItemTouchHelper(makeMoveCallback(Instrument.instruments)) helper.attachToRecyclerView(binding.recyclerView) val builder = AlertDialog.Builder(binding.root.context) diff --git a/app/src/main/java/com/lukas/music/util/UIUtil.kt b/app/src/main/java/com/lukas/music/util/UIUtil.kt index b67d0ea..72e0681 100644 --- a/app/src/main/java/com/lukas/music/util/UIUtil.kt +++ b/app/src/main/java/com/lukas/music/util/UIUtil.kt @@ -13,6 +13,8 @@ import android.view.View import android.widget.* import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.RecyclerView import com.lukas.music.R import kotlin.reflect.KMutableProperty0 @@ -138,4 +140,32 @@ } callback(it) } +} + +fun makeMoveCallback( + list: MutableList, + callback: (Int, Int) -> Unit = { _, _ -> } +): ItemTouchHelper.SimpleCallback { + return object : ItemTouchHelper.SimpleCallback( + ItemTouchHelper.UP or ItemTouchHelper.DOWN, + 0 + ) { + override fun onMove( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder, + target: RecyclerView.ViewHolder + ): Boolean { + val adapter = recyclerView.adapter + val startPosition = viewHolder.adapterPosition + val endPosition = target.adapterPosition + val item = list[startPosition] + list.removeAt(startPosition) + list.add(endPosition, item) + adapter!!.notifyItemMoved(startPosition, endPosition) + callback(startPosition, endPosition) + return true + } + + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {} + } } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 7310a37..d8b0f5d 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,8 +9,12 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); - lowPass->host = host; + auto *filter = new LowPass(); + filter->host = host; + effects.push_back(filter); + auto *noise = new Noise(); noise->host = host; + effects.push_back(noise); } void multiply(float *target, float *modulation, uint32_t size) { @@ -44,8 +48,9 @@ void Instrument::render(float *buffer, uint32_t count) { float *waveform = wave->render(count); - processEffect(waveform, count, lowPass); - processEffect(waveform, count, noise); + for (auto effect: effects) { + processEffect(waveform, count, effect); + } multiply(waveform, envelope->render(count), count); multiply(waveform, volume, count); add(buffer, waveform, count); @@ -54,8 +59,10 @@ void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); - lowPass->frequency = frequency; - lowPass->update(); + for (auto effect: effects) { + effect->frequency = frequency; + effect->update(); + } } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 077bfe0..df45330 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -17,8 +17,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; - LowPass *lowPass = new LowPass(); - Noise *noise = new Noise(); + std::list effects; float volume = 0; void render(float *buffer, uint32_t count); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 7ccc60b..2cc9efa 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -101,16 +101,24 @@ jfloat influence, jfloat parameter1) { Instrument *instrument = getInstrument(id); - Effect *effect; - switch (effect_number) { - case 0: - effect = instrument->lowPass; - break; - case 1: - effect = instrument->noise; - break; - } + auto iterator = instrument->effects.begin(); + std::advance(iterator, effect_number); + auto *effect = *iterator; effect->influence = influence; effect->parameter1 = parameter1; } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_moveEffects(JNIEnv *env, jobject thiz, jint id, + jint from, jint to) { + Instrument *instrument = getInstrument(id); + auto source = instrument->effects.begin(); + std::advance(source, from); + auto destination = instrument->effects.begin(); + std::advance(destination, to); + if (instrument->effects.size() == to + 1) { + destination = instrument->effects.end(); + } + instrument->effects.splice(destination, instrument->effects, source); +} } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index b9fa2c1..70d173a 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -18,7 +18,7 @@ abstract class Instrument(var name: String) { var voice: Voice = Voice(this) var envelope = Envelope(this) - val effects = Array(EffectType.VALUES.size) { + val effects = MutableList(EffectType.VALUES.size) { Effect(EffectType.VALUES[it], this) } @@ -33,6 +33,7 @@ abstract fun updateEnvelope() abstract fun updateEffects() abstract fun isPlaying(note: Note): Boolean + abstract fun moveEffects(from: Int, to: Int) companion object { val instruments = mutableListOf() diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index d24f474..40f3e97 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -78,15 +78,19 @@ ) } - fun applyEffectAttributes(effect: Effect) { + fun applyEffectAttributes(instrument: Instrument, effect: Effect) { applyEffectAttributes( id, - effect.type.ordinal, + instrument.effects.indexOf(effect), if (effect.active) effect.influence.value else 0f, - effect.parameters[0].value + effect.parameters[0]?.value ?: 0f ) } + fun moveEffects(from: Int, to: Int) { + moveEffects(id, from, to) + } + private external fun createInstrument(): Int private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) @@ -107,4 +111,6 @@ influence: Float, parameter1: Float ) + + private external fun moveEffects(id: Int, from: Int, to: 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 e631548..b702455 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -33,14 +33,6 @@ internalInstrument.muted = value } - override fun startNote(note: Note) { - internalInstrument.startNote(note) - } - - override fun stop() { - internalInstrument.endNote() - } - override fun stopNote(note: Note) { if (note == internalInstrument.note) { stop() @@ -51,15 +43,15 @@ internalInstrument.destroy() } - override fun updateEnvelope() { - internalInstrument.applyEnvelope(envelope) - } - override fun updateEffects() { for (effect in effects) { - internalInstrument.applyEffectAttributes(effect) + internalInstrument.applyEffectAttributes(this, effect) } } override fun isPlaying(note: Note): Boolean = internalInstrument.note == note + override fun moveEffects(from: Int, to: Int) = internalInstrument.moveEffects(from, to) + override fun updateEnvelope() = internalInstrument.applyEnvelope(envelope) + override fun startNote(note: Note) = internalInstrument.startNote(note) + override fun stop() = internalInstrument.endNote() } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 7beb64c..b33c439 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -86,7 +86,7 @@ override fun updateEffects() { for (instrument in internalInstruments) { for (effect in effects) { - instrument.applyEffectAttributes(effect) + instrument.applyEffectAttributes(this, effect) } } } @@ -99,4 +99,10 @@ } return false } + + override fun moveEffects(from: Int, to: Int) { + for (instrument in internalInstruments) { + instrument.moveEffects(from, to) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt index f921b1b..f659a8c 100644 --- a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt +++ b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt @@ -14,7 +14,9 @@ class Effect(val type: EffectType, private val instrument: Instrument) { val parameters = Array(type.parameterDescriptions.size) { - EffectParameter(type.parameterDescriptions[it], instrument) + type.parameterDescriptions[it]?.let { parameterDescription -> + EffectParameter(parameterDescription, instrument) + } } val influence = EffectParameter(influenceDescription, instrument) diff --git a/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt b/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt index 8af39ea..963d4e3 100644 --- a/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt +++ b/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt @@ -13,8 +13,8 @@ import com.lukas.music.util.format enum class EffectType( - val title: String, - val parameterDescriptions: Array + private val title: String, + val parameterDescriptions: Array ) { LowPass("low pass filter", arrayOf( @@ -24,9 +24,7 @@ )), Noise("noise", arrayOf( - EffectParameterDescription(0f, 1f, 0f) { - "unused" - } + null ) ) ; diff --git a/app/src/main/java/com/lukas/music/ui/adapters/EffectsAdapter.kt b/app/src/main/java/com/lukas/music/ui/adapters/EffectsAdapter.kt new file mode 100644 index 0000000..730eabd --- /dev/null +++ b/app/src/main/java/com/lukas/music/ui/adapters/EffectsAdapter.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 Lukas Eisenhauer + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + +package com.lukas.music.ui.adapters + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.lukas.music.databinding.FragmentEffectBinding +import com.lukas.music.instruments.Instrument +import com.lukas.music.ui.fragments.EditEffectsFragment +import com.lukas.music.ui.fragments.EffectFragment + +class EffectsAdapter(private val parent: EditEffectsFragment, private val instrument: Instrument) : + RecyclerView.Adapter() { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EffectFragment { + val context = parent.context + val inflater = LayoutInflater.from(context) + val binding = FragmentEffectBinding.inflate(inflater, parent, false) + return EffectFragment(binding) + } + + override fun onBindViewHolder(holder: EffectFragment, position: Int) { + holder.setEffect(instrument.effects[position]) + } + + override fun getItemCount(): Int { + return instrument.effects.size + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/ui/fragments/EditEffectsFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/EditEffectsFragment.kt index 6e359e4..0d532c2 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/EditEffectsFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/EditEffectsFragment.kt @@ -14,9 +14,13 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.LinearLayoutManager import com.lukas.music.databinding.FragmentEditEffectsBinding import com.lukas.music.instruments.Instrument +import com.lukas.music.ui.adapters.EffectsAdapter import com.lukas.music.util.EasyDialogFragment +import com.lukas.music.util.makeMoveCallback class EditEffectsFragment(private val instrument: Instrument) : EasyDialogFragment() { @@ -25,11 +29,12 @@ savedInstanceState: Bundle? ): View? { binding = FragmentEditEffectsBinding.inflate(inflater) - for (effect in instrument.effects) { - val effectEditor = EffectFragment(effect) - childFragmentManager.beginTransaction().add(binding.effectsDisplay.id, effectEditor) - .commit() - } + binding.effectsDisplay.adapter = EffectsAdapter(this, instrument) + binding.effectsDisplay.layoutManager = LinearLayoutManager(context) + val helper = ItemTouchHelper(makeMoveCallback(instrument.effects) { from, to -> + instrument.moveEffects(from, to) + }) + helper.attachToRecyclerView(binding.effectsDisplay) binding.closeButton.setOnClickListener { dismiss() } diff --git a/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt index ebf4cb1..7b13ed6 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt @@ -10,25 +10,18 @@ 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 androidx.recyclerview.widget.RecyclerView import com.lukas.music.R import com.lukas.music.databinding.FragmentEffectBinding import com.lukas.music.instruments.effect.Effect import com.lukas.music.util.setupToggle import com.lukas.music.util.smartSetup -class EffectFragment(private val effect: Effect) : Fragment() { - lateinit var binding: FragmentEffectBinding - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - binding = FragmentEffectBinding.inflate(inflater) +class EffectFragment(val binding: FragmentEffectBinding) : RecyclerView.ViewHolder( + binding.root +) { + fun setEffect(effect: Effect) { binding.effectName.text = effect.type.toString() binding.activeButton.setupToggle(effect::active, R.color.blue) { binding.activeButton.text = if (it) "ON" else "OFF" @@ -37,10 +30,15 @@ binding.influenceSeekBar.smartSetup(0, 100, effect.influence::percentageValue) { binding.influenceText.text = effect.influence.description.text(effect.influence) } - binding.parameter1SeekBar.smartSetup(0, 100, effect.parameters[0]::percentageValue) { - binding.parameter1Text.text = - effect.parameters[0].description.text(effect.parameters[0]) + binding.parameter1SeekBar.visibility = + if (effect.parameters[0] == null) View.GONE else View.VISIBLE + binding.parameter1Text.visibility = + if (effect.parameters[0] == null) View.GONE else View.VISIBLE + effect.parameters[0]?.let { + binding.parameter1SeekBar.smartSetup(0, 100, it::percentageValue) { + binding.parameter1Text.text = + effect.parameters[0]!!.description.text(effect.parameters[0]!!) + } } - return binding.root } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/ui/fragments/InstrumentListFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/InstrumentListFragment.kt index 1d35c10..0b66892 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/InstrumentListFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/InstrumentListFragment.kt @@ -24,6 +24,7 @@ import com.lukas.music.instruments.MonoInstrument import com.lukas.music.instruments.PolyInstrument import com.lukas.music.ui.adapters.InstrumentAdapter +import com.lukas.music.util.makeMoveCallback class InstrumentListFragment : Fragment() { lateinit var binding: FragmentInstrumentListBinding @@ -35,32 +36,7 @@ binding = FragmentInstrumentListBinding.inflate(inflater) binding.recyclerView.adapter = InstrumentAdapter(this) binding.recyclerView.layoutManager = LinearLayoutManager(context) - val callback = object : ItemTouchHelper.SimpleCallback( - ItemTouchHelper.UP or ItemTouchHelper.DOWN, - 0 - ) { - override fun onMove( - recyclerView: RecyclerView, - viewHolder: RecyclerView.ViewHolder, - target: RecyclerView.ViewHolder - ): Boolean { - val adapter = recyclerView.adapter as InstrumentAdapter - val startPosition = viewHolder.adapterPosition - val endPosition = target.adapterPosition - val instrument = Instrument.instruments[startPosition] - Instrument.instruments.removeAt(startPosition) - if (endPosition < startPosition) { - Instrument.instruments.add(endPosition + 1, instrument) - } else { - Instrument.instruments.add(endPosition - 1, instrument) - } - adapter.notifyItemMoved(startPosition, endPosition) - return true - } - - override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {} - } - val helper = ItemTouchHelper(callback) + val helper = ItemTouchHelper(makeMoveCallback(Instrument.instruments)) helper.attachToRecyclerView(binding.recyclerView) val builder = AlertDialog.Builder(binding.root.context) diff --git a/app/src/main/java/com/lukas/music/util/UIUtil.kt b/app/src/main/java/com/lukas/music/util/UIUtil.kt index b67d0ea..72e0681 100644 --- a/app/src/main/java/com/lukas/music/util/UIUtil.kt +++ b/app/src/main/java/com/lukas/music/util/UIUtil.kt @@ -13,6 +13,8 @@ import android.view.View import android.widget.* import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.RecyclerView import com.lukas.music.R import kotlin.reflect.KMutableProperty0 @@ -138,4 +140,32 @@ } callback(it) } +} + +fun makeMoveCallback( + list: MutableList, + callback: (Int, Int) -> Unit = { _, _ -> } +): ItemTouchHelper.SimpleCallback { + return object : ItemTouchHelper.SimpleCallback( + ItemTouchHelper.UP or ItemTouchHelper.DOWN, + 0 + ) { + override fun onMove( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder, + target: RecyclerView.ViewHolder + ): Boolean { + val adapter = recyclerView.adapter + val startPosition = viewHolder.adapterPosition + val endPosition = target.adapterPosition + val item = list[startPosition] + list.removeAt(startPosition) + list.add(endPosition, item) + adapter!!.notifyItemMoved(startPosition, endPosition) + callback(startPosition, endPosition) + return true + } + + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {} + } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_edit_effects.xml b/app/src/main/res/layout/fragment_edit_effects.xml index 46d4648..051d66f 100644 --- a/app/src/main/res/layout/fragment_edit_effects.xml +++ b/app/src/main/res/layout/fragment_edit_effects.xml @@ -43,7 +43,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/effectsDisplay" /> - + app:layout_constraintTop_toBottomOf="@+id/textView10"> \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 7310a37..d8b0f5d 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,8 +9,12 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); - lowPass->host = host; + auto *filter = new LowPass(); + filter->host = host; + effects.push_back(filter); + auto *noise = new Noise(); noise->host = host; + effects.push_back(noise); } void multiply(float *target, float *modulation, uint32_t size) { @@ -44,8 +48,9 @@ void Instrument::render(float *buffer, uint32_t count) { float *waveform = wave->render(count); - processEffect(waveform, count, lowPass); - processEffect(waveform, count, noise); + for (auto effect: effects) { + processEffect(waveform, count, effect); + } multiply(waveform, envelope->render(count), count); multiply(waveform, volume, count); add(buffer, waveform, count); @@ -54,8 +59,10 @@ void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); - lowPass->frequency = frequency; - lowPass->update(); + for (auto effect: effects) { + effect->frequency = frequency; + effect->update(); + } } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 077bfe0..df45330 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -17,8 +17,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; - LowPass *lowPass = new LowPass(); - Noise *noise = new Noise(); + std::list effects; float volume = 0; void render(float *buffer, uint32_t count); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 7ccc60b..2cc9efa 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -101,16 +101,24 @@ jfloat influence, jfloat parameter1) { Instrument *instrument = getInstrument(id); - Effect *effect; - switch (effect_number) { - case 0: - effect = instrument->lowPass; - break; - case 1: - effect = instrument->noise; - break; - } + auto iterator = instrument->effects.begin(); + std::advance(iterator, effect_number); + auto *effect = *iterator; effect->influence = influence; effect->parameter1 = parameter1; } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_moveEffects(JNIEnv *env, jobject thiz, jint id, + jint from, jint to) { + Instrument *instrument = getInstrument(id); + auto source = instrument->effects.begin(); + std::advance(source, from); + auto destination = instrument->effects.begin(); + std::advance(destination, to); + if (instrument->effects.size() == to + 1) { + destination = instrument->effects.end(); + } + instrument->effects.splice(destination, instrument->effects, source); +} } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index b9fa2c1..70d173a 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -18,7 +18,7 @@ abstract class Instrument(var name: String) { var voice: Voice = Voice(this) var envelope = Envelope(this) - val effects = Array(EffectType.VALUES.size) { + val effects = MutableList(EffectType.VALUES.size) { Effect(EffectType.VALUES[it], this) } @@ -33,6 +33,7 @@ abstract fun updateEnvelope() abstract fun updateEffects() abstract fun isPlaying(note: Note): Boolean + abstract fun moveEffects(from: Int, to: Int) companion object { val instruments = mutableListOf() diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index d24f474..40f3e97 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -78,15 +78,19 @@ ) } - fun applyEffectAttributes(effect: Effect) { + fun applyEffectAttributes(instrument: Instrument, effect: Effect) { applyEffectAttributes( id, - effect.type.ordinal, + instrument.effects.indexOf(effect), if (effect.active) effect.influence.value else 0f, - effect.parameters[0].value + effect.parameters[0]?.value ?: 0f ) } + fun moveEffects(from: Int, to: Int) { + moveEffects(id, from, to) + } + private external fun createInstrument(): Int private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) @@ -107,4 +111,6 @@ influence: Float, parameter1: Float ) + + private external fun moveEffects(id: Int, from: Int, to: 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 e631548..b702455 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -33,14 +33,6 @@ internalInstrument.muted = value } - override fun startNote(note: Note) { - internalInstrument.startNote(note) - } - - override fun stop() { - internalInstrument.endNote() - } - override fun stopNote(note: Note) { if (note == internalInstrument.note) { stop() @@ -51,15 +43,15 @@ internalInstrument.destroy() } - override fun updateEnvelope() { - internalInstrument.applyEnvelope(envelope) - } - override fun updateEffects() { for (effect in effects) { - internalInstrument.applyEffectAttributes(effect) + internalInstrument.applyEffectAttributes(this, effect) } } override fun isPlaying(note: Note): Boolean = internalInstrument.note == note + override fun moveEffects(from: Int, to: Int) = internalInstrument.moveEffects(from, to) + override fun updateEnvelope() = internalInstrument.applyEnvelope(envelope) + override fun startNote(note: Note) = internalInstrument.startNote(note) + override fun stop() = internalInstrument.endNote() } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 7beb64c..b33c439 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -86,7 +86,7 @@ override fun updateEffects() { for (instrument in internalInstruments) { for (effect in effects) { - instrument.applyEffectAttributes(effect) + instrument.applyEffectAttributes(this, effect) } } } @@ -99,4 +99,10 @@ } return false } + + override fun moveEffects(from: Int, to: Int) { + for (instrument in internalInstruments) { + instrument.moveEffects(from, to) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt index f921b1b..f659a8c 100644 --- a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt +++ b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt @@ -14,7 +14,9 @@ class Effect(val type: EffectType, private val instrument: Instrument) { val parameters = Array(type.parameterDescriptions.size) { - EffectParameter(type.parameterDescriptions[it], instrument) + type.parameterDescriptions[it]?.let { parameterDescription -> + EffectParameter(parameterDescription, instrument) + } } val influence = EffectParameter(influenceDescription, instrument) diff --git a/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt b/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt index 8af39ea..963d4e3 100644 --- a/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt +++ b/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt @@ -13,8 +13,8 @@ import com.lukas.music.util.format enum class EffectType( - val title: String, - val parameterDescriptions: Array + private val title: String, + val parameterDescriptions: Array ) { LowPass("low pass filter", arrayOf( @@ -24,9 +24,7 @@ )), Noise("noise", arrayOf( - EffectParameterDescription(0f, 1f, 0f) { - "unused" - } + null ) ) ; diff --git a/app/src/main/java/com/lukas/music/ui/adapters/EffectsAdapter.kt b/app/src/main/java/com/lukas/music/ui/adapters/EffectsAdapter.kt new file mode 100644 index 0000000..730eabd --- /dev/null +++ b/app/src/main/java/com/lukas/music/ui/adapters/EffectsAdapter.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 Lukas Eisenhauer + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + +package com.lukas.music.ui.adapters + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.lukas.music.databinding.FragmentEffectBinding +import com.lukas.music.instruments.Instrument +import com.lukas.music.ui.fragments.EditEffectsFragment +import com.lukas.music.ui.fragments.EffectFragment + +class EffectsAdapter(private val parent: EditEffectsFragment, private val instrument: Instrument) : + RecyclerView.Adapter() { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EffectFragment { + val context = parent.context + val inflater = LayoutInflater.from(context) + val binding = FragmentEffectBinding.inflate(inflater, parent, false) + return EffectFragment(binding) + } + + override fun onBindViewHolder(holder: EffectFragment, position: Int) { + holder.setEffect(instrument.effects[position]) + } + + override fun getItemCount(): Int { + return instrument.effects.size + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/ui/fragments/EditEffectsFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/EditEffectsFragment.kt index 6e359e4..0d532c2 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/EditEffectsFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/EditEffectsFragment.kt @@ -14,9 +14,13 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.LinearLayoutManager import com.lukas.music.databinding.FragmentEditEffectsBinding import com.lukas.music.instruments.Instrument +import com.lukas.music.ui.adapters.EffectsAdapter import com.lukas.music.util.EasyDialogFragment +import com.lukas.music.util.makeMoveCallback class EditEffectsFragment(private val instrument: Instrument) : EasyDialogFragment() { @@ -25,11 +29,12 @@ savedInstanceState: Bundle? ): View? { binding = FragmentEditEffectsBinding.inflate(inflater) - for (effect in instrument.effects) { - val effectEditor = EffectFragment(effect) - childFragmentManager.beginTransaction().add(binding.effectsDisplay.id, effectEditor) - .commit() - } + binding.effectsDisplay.adapter = EffectsAdapter(this, instrument) + binding.effectsDisplay.layoutManager = LinearLayoutManager(context) + val helper = ItemTouchHelper(makeMoveCallback(instrument.effects) { from, to -> + instrument.moveEffects(from, to) + }) + helper.attachToRecyclerView(binding.effectsDisplay) binding.closeButton.setOnClickListener { dismiss() } diff --git a/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt index ebf4cb1..7b13ed6 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt @@ -10,25 +10,18 @@ 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 androidx.recyclerview.widget.RecyclerView import com.lukas.music.R import com.lukas.music.databinding.FragmentEffectBinding import com.lukas.music.instruments.effect.Effect import com.lukas.music.util.setupToggle import com.lukas.music.util.smartSetup -class EffectFragment(private val effect: Effect) : Fragment() { - lateinit var binding: FragmentEffectBinding - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - binding = FragmentEffectBinding.inflate(inflater) +class EffectFragment(val binding: FragmentEffectBinding) : RecyclerView.ViewHolder( + binding.root +) { + fun setEffect(effect: Effect) { binding.effectName.text = effect.type.toString() binding.activeButton.setupToggle(effect::active, R.color.blue) { binding.activeButton.text = if (it) "ON" else "OFF" @@ -37,10 +30,15 @@ binding.influenceSeekBar.smartSetup(0, 100, effect.influence::percentageValue) { binding.influenceText.text = effect.influence.description.text(effect.influence) } - binding.parameter1SeekBar.smartSetup(0, 100, effect.parameters[0]::percentageValue) { - binding.parameter1Text.text = - effect.parameters[0].description.text(effect.parameters[0]) + binding.parameter1SeekBar.visibility = + if (effect.parameters[0] == null) View.GONE else View.VISIBLE + binding.parameter1Text.visibility = + if (effect.parameters[0] == null) View.GONE else View.VISIBLE + effect.parameters[0]?.let { + binding.parameter1SeekBar.smartSetup(0, 100, it::percentageValue) { + binding.parameter1Text.text = + effect.parameters[0]!!.description.text(effect.parameters[0]!!) + } } - return binding.root } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/ui/fragments/InstrumentListFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/InstrumentListFragment.kt index 1d35c10..0b66892 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/InstrumentListFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/InstrumentListFragment.kt @@ -24,6 +24,7 @@ import com.lukas.music.instruments.MonoInstrument import com.lukas.music.instruments.PolyInstrument import com.lukas.music.ui.adapters.InstrumentAdapter +import com.lukas.music.util.makeMoveCallback class InstrumentListFragment : Fragment() { lateinit var binding: FragmentInstrumentListBinding @@ -35,32 +36,7 @@ binding = FragmentInstrumentListBinding.inflate(inflater) binding.recyclerView.adapter = InstrumentAdapter(this) binding.recyclerView.layoutManager = LinearLayoutManager(context) - val callback = object : ItemTouchHelper.SimpleCallback( - ItemTouchHelper.UP or ItemTouchHelper.DOWN, - 0 - ) { - override fun onMove( - recyclerView: RecyclerView, - viewHolder: RecyclerView.ViewHolder, - target: RecyclerView.ViewHolder - ): Boolean { - val adapter = recyclerView.adapter as InstrumentAdapter - val startPosition = viewHolder.adapterPosition - val endPosition = target.adapterPosition - val instrument = Instrument.instruments[startPosition] - Instrument.instruments.removeAt(startPosition) - if (endPosition < startPosition) { - Instrument.instruments.add(endPosition + 1, instrument) - } else { - Instrument.instruments.add(endPosition - 1, instrument) - } - adapter.notifyItemMoved(startPosition, endPosition) - return true - } - - override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {} - } - val helper = ItemTouchHelper(callback) + val helper = ItemTouchHelper(makeMoveCallback(Instrument.instruments)) helper.attachToRecyclerView(binding.recyclerView) val builder = AlertDialog.Builder(binding.root.context) diff --git a/app/src/main/java/com/lukas/music/util/UIUtil.kt b/app/src/main/java/com/lukas/music/util/UIUtil.kt index b67d0ea..72e0681 100644 --- a/app/src/main/java/com/lukas/music/util/UIUtil.kt +++ b/app/src/main/java/com/lukas/music/util/UIUtil.kt @@ -13,6 +13,8 @@ import android.view.View import android.widget.* import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.RecyclerView import com.lukas.music.R import kotlin.reflect.KMutableProperty0 @@ -138,4 +140,32 @@ } callback(it) } +} + +fun makeMoveCallback( + list: MutableList, + callback: (Int, Int) -> Unit = { _, _ -> } +): ItemTouchHelper.SimpleCallback { + return object : ItemTouchHelper.SimpleCallback( + ItemTouchHelper.UP or ItemTouchHelper.DOWN, + 0 + ) { + override fun onMove( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder, + target: RecyclerView.ViewHolder + ): Boolean { + val adapter = recyclerView.adapter + val startPosition = viewHolder.adapterPosition + val endPosition = target.adapterPosition + val item = list[startPosition] + list.removeAt(startPosition) + list.add(endPosition, item) + adapter!!.notifyItemMoved(startPosition, endPosition) + callback(startPosition, endPosition) + return true + } + + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {} + } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_edit_effects.xml b/app/src/main/res/layout/fragment_edit_effects.xml index 46d4648..051d66f 100644 --- a/app/src/main/res/layout/fragment_edit_effects.xml +++ b/app/src/main/res/layout/fragment_edit_effects.xml @@ -43,7 +43,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/effectsDisplay" /> - + app:layout_constraintTop_toBottomOf="@+id/textView10"> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_effect.xml b/app/src/main/res/layout/fragment_effect.xml index 7f305be..b563832 100644 --- a/app/src/main/res/layout/fragment_effect.xml +++ b/app/src/main/res/layout/fragment_effect.xml @@ -12,7 +12,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="match_parent" + android:layout_height="wrap_content" tools:context=".ui.fragments.EffectFragment"> + android:layout_height="match_parent" + android:padding="16dp"> @@ -45,7 +45,6 @@ android:id="@+id/influenceText" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginStart="16dp" android:text="@string/placeholder" app:layout_constraintBottom_toBottomOf="@+id/influenceSeekBar" app:layout_constraintEnd_toEndOf="@+id/parameter1Text" @@ -56,9 +55,6 @@ android:id="@+id/effectName" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginStart="16dp" - android:layout_marginTop="16dp" - android:layout_marginEnd="16dp" android:text="@string/placeholder" android:textSize="16sp" app:layout_constraintEnd_toEndOf="parent" @@ -70,8 +66,6 @@ android:layout_width="0dp" android:layout_height="40dp" android:layout_marginTop="16dp" - android:layout_marginBottom="16dp" - app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="@+id/influenceSeekBar" app:layout_constraintStart_toStartOf="@+id/influenceSeekBar" app:layout_constraintTop_toBottomOf="@+id/influenceSeekBar" /> @@ -80,7 +74,6 @@ android:id="@+id/parameter1Text" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginStart="16dp" android:layout_marginEnd="16dp" android:text="@string/placeholder" app:layout_constraintBottom_toBottomOf="@+id/parameter1SeekBar"