From 598499e146637f07c75e6da4e178dc57a68ede0d Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 8 Jan 2024 13:22:44 +0300 Subject: [PATCH 1/2] Implementation of the Touch Drag Scroll feature --- .idea/modules.xml | 3 + .../android/ui/PianoActivity2.java | 4 + .../android/ui/SettingsActivity.java | 9 ++ .../widgets/keyboard/KeyboardView.java | 132 +++++++++++++++--- app/src/main/res/values/strings.xml | 15 +- app/src/main/res/xml/preferences.xml | 8 ++ 6 files changed, 147 insertions(+), 24 deletions(-) diff --git a/.idea/modules.xml b/.idea/modules.xml index cf6b968..40ad31a 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -4,6 +4,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/java/com/manichord/synthesizer/android/ui/PianoActivity2.java b/app/src/main/java/com/manichord/synthesizer/android/ui/PianoActivity2.java index 566d969..9ddfb79 100644 --- a/app/src/main/java/com/manichord/synthesizer/android/ui/PianoActivity2.java +++ b/app/src/main/java/com/manichord/synthesizer/android/ui/PianoActivity2.java @@ -166,6 +166,7 @@ protected void onResume() { onSharedPreferenceChanged(prefs, "keyboard_type"); onSharedPreferenceChanged(prefs, "vel_sens"); onSharedPreferenceChanged(prefs, "midi_channel"); + onSharedPreferenceChanged(prefs, "touch_drag_action"); } @Override @@ -192,6 +193,9 @@ public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { Log.d("PianoActivity2", "cannot set current channel no Synth service"); } Log.d("PianoActivity2", "set current channel:" + currentChannel); + } else if (key.equals("touch_drag_action")) { + String touchDragAction = prefs.getString(key, "TDA_PlayNotes"); + keyboard_.setTouchDragAction(KeyboardView.TouchDragAction.toTouchDragAction(touchDragAction)); } } diff --git a/app/src/main/java/com/manichord/synthesizer/android/ui/SettingsActivity.java b/app/src/main/java/com/manichord/synthesizer/android/ui/SettingsActivity.java index 0e621b3..8da062b 100644 --- a/app/src/main/java/com/manichord/synthesizer/android/ui/SettingsActivity.java +++ b/app/src/main/java/com/manichord/synthesizer/android/ui/SettingsActivity.java @@ -32,6 +32,15 @@ public boolean onPreferenceChange(Preference pref, Object newVal) { return true; } }); + ListPreference touchDragActionPref = (ListPreference)findPreference("touch_drag_action"); + updateListSummary(touchDragActionPref, touchDragActionPref.getValue()); + Log.d("SettingsActivity", "touch drag action:"+touchDragActionPref.getValue()); + touchDragActionPref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { + public boolean onPreferenceChange(Preference pref, Object newVal) { + updateListSummary(pref, newVal.toString()); + return true; + } + }); } diff --git a/app/src/main/java/com/manichord/synthesizer/android/widgets/keyboard/KeyboardView.java b/app/src/main/java/com/manichord/synthesizer/android/widgets/keyboard/KeyboardView.java index 5cdc242..0476aa8 100644 --- a/app/src/main/java/com/manichord/synthesizer/android/widgets/keyboard/KeyboardView.java +++ b/app/src/main/java/com/manichord/synthesizer/android/widgets/keyboard/KeyboardView.java @@ -1,4 +1,5 @@ /* + * * Copyright 2013 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,16 +27,33 @@ import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; +import android.util.Log; +import java.lang.Math; import com.manichord.synthesizer.core.midi.MidiListener; public class KeyboardView extends View { + + public enum TouchDragAction { + TDA_PlayNotes, TDA_ScrollKeyboard; + + public static TouchDragAction toTouchDragAction(String TouchDragActionString) { + try { + return valueOf(TouchDragActionString); + } catch (Exception ex) { + return TDA_PlayNotes; + } + } + } + public KeyboardView(Context context, AttributeSet attrs) { super(context, attrs); nKeys_ = 96; firstKey_ = 12; noteStatus_ = new byte[128]; noteForFinger_ = new int[FINGERS]; + touchCurrentX = new float[FINGERS]; + for (int i = 0; i < FINGERS; i++) { noteForFinger_[i] = -1; } @@ -53,6 +71,8 @@ public KeyboardView(Context context, AttributeSet attrs) { setKeyboardSpec(KeyboardSpec.make2Row()); velSens_ = 0.5f; velAvg_ = 64; + + touchDragAction_ = TouchDragAction.TDA_PlayNotes; } public void setKeyboardSpec(KeyboardSpec keyboardSpec) { @@ -61,6 +81,11 @@ public void setKeyboardSpec(KeyboardSpec keyboardSpec) { invalidate(); } + public void setTouchDragAction(TouchDragAction action) + { + touchDragAction_ = action; + } + public void setMidiListener(MidiListener listener) { midiListener_ = listener; } @@ -217,13 +242,24 @@ private int computeVelocity(float pressure) { private boolean onTouchDown(int id, float x, float y, float pressure) { int note = hitTest(x, y); + if (note >= 0 && noteStatus_[note] == 0) { int velocity = computeVelocity(pressure); + noteForFinger_[id] = note; + noteStatus_[note] = (byte)velocity; if (midiListener_ != null) { midiListener_.onNoteOn(0, note, velocity); } + + if (touchDragAction_ == TouchDragAction.TDA_ScrollKeyboard) + { + touchCurrentX[id] = x; + if (notesPressed() == 1) + touchTrackingOffset = offset_; + } + return true; } return false; @@ -232,43 +268,88 @@ private boolean onTouchDown(int id, float x, float y, float pressure) { private boolean onTouchUp(int id, float x, float y, float pressure) { int note = noteForFinger_[id]; if (note >= 0) { + int velocity = noteStatus_[note]; if (midiListener_ != null) { midiListener_.onNoteOff(0, note, velocity); } noteForFinger_[id] = -1; noteStatus_[note] = 0; + return true; } return false; } - private boolean onTouchMove(int id, float x, float y, float pressure) { - int oldNote = noteForFinger_[id]; - int newNote = hitTest(x, y); - if (newNote != -1 && newNote != oldNote && noteStatus_[newNote] == 0) { - // keep consistent velocity; new is likely to be too high - if (oldNote >= 0) { - int velocity = noteStatus_[oldNote]; - if (midiListener_ != null) { - midiListener_.onNoteOff(0, oldNote, velocity); - midiListener_.onNoteOn(0, newNote, velocity); - } - noteForFinger_[id] = newNote; - noteStatus_[oldNote] = 0; - noteStatus_[newNote] = (byte)velocity; - } else { - // moving onto active note from dead zone - int velocity = 64; - if (midiListener_ != null) { - midiListener_.onNoteOn(0, newNote, velocity); + private int notesPressed() + { + int res = 0; + for (int i = 0; i < FINGERS; i++) { + if (noteForFinger_[i] != -1) + res++; + } + return res; + } + + private boolean handleScrollTouch(int id, float x) + { + if (touchDragAction_ == TouchDragAction.TDA_ScrollKeyboard) + { + if (noteForFinger_[id] != -1) + { + /* + The effective scroll offset is the current touch devided by the number of current active touches + This correspondes to the following cases: + - A single touch: should scroll exactly the same amount as the touch moved, no perceived mismatch between expectations and reality + - Multi-touch: either the player moves the fingers more or less synchronously or some of them moves more or less comparing to the others + The latter might introduce the sense that the scrolling moves too much or too little because of the mathematics described. + */ + touchTrackingOffset = touchTrackingOffset + (x - touchCurrentX[id]) / notesPressed(); + + touchCurrentX[id] = x; + + if (Math.abs(touchTrackingOffset - offset_) >= 1) + { + setScrollZoom(touchTrackingOffset, zoom_); } - noteForFinger_[id] = newNote; - noteStatus_[newNote] = (byte)velocity; } return true; + } else + return false; + } + + private boolean onTouchMove(int id, float x, float y, float pressure) { + + if (handleScrollTouch(id, x)) { + return false; + } else { + int oldNote = noteForFinger_[id]; + int newNote = hitTest(x, y); + if (newNote != -1 && newNote != oldNote && noteStatus_[newNote] == 0) { + // keep consistent velocity; new is likely to be too high + if (oldNote >= 0) { + int velocity = noteStatus_[oldNote]; + if (midiListener_ != null) { + midiListener_.onNoteOff(0, oldNote, velocity); + midiListener_.onNoteOn(0, newNote, velocity); + } + noteForFinger_[id] = newNote; + noteStatus_[oldNote] = 0; + noteStatus_[newNote] = (byte)velocity; + } else { + // moving onto active note from dead zone + int velocity = 64; + if (midiListener_ != null) { + midiListener_.onNoteOn(0, newNote, velocity); + } + noteForFinger_[id] = newNote; + noteStatus_[newNote] = (byte)velocity; + } + return true; + } + + return false; } - return false; } private static String noteString(int note) { @@ -276,6 +357,9 @@ private static String noteString(int note) { return NOTE_NAMES[note % 12] + Integer.toString(octave); } + + private static final String TAG = "KEYBWIDGET"; + private float velSens_; private float velAvg_; @@ -287,6 +371,10 @@ private static String noteString(int note) { private float textSize_; private float keyboardScale_; + private float[] touchCurrentX; // Array containing all current x positions of the note touches + private float touchTrackingOffset; // Tracking offset keeps the accurate offset x position and passes it to the scroll when the threashold of 1 is reached + private TouchDragAction touchDragAction_; // The current behavior of the touch-drag action, either the classic way (playing new notes while touch is moving ot scrolling the keyboard) + private float offset_; private float zoom_; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 21dcaee..4705c1f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -89,12 +89,23 @@ 2 Row (Piano) 3 Row 3 Row Chromatic - + 2row 3row 3chrome - + + + + Play notes + Scroll keyboard + + + TDA_PlayNotes + TDA_ScrollKeyboard + + + 2row Velocity average value diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index d48d3e8..f77e1ae 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -25,4 +25,12 @@ android:entries="@array/pref_midi_channel_entries" android:entryValues="@array/pref_midi_channel_values" android:defaultValue="@string/pref_midi_channel_default" /> + + + From 7e30dc0e6be959b7267f14d75d9ef66edb71e614 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 8 Jan 2024 13:27:56 +0300 Subject: [PATCH 2/2] Reordering of new routines for better diff --- .../widgets/keyboard/KeyboardView.java | 74 +++++++++---------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/com/manichord/synthesizer/android/widgets/keyboard/KeyboardView.java b/app/src/main/java/com/manichord/synthesizer/android/widgets/keyboard/KeyboardView.java index 0476aa8..d9cd6ec 100644 --- a/app/src/main/java/com/manichord/synthesizer/android/widgets/keyboard/KeyboardView.java +++ b/app/src/main/java/com/manichord/synthesizer/android/widgets/keyboard/KeyboardView.java @@ -281,43 +281,6 @@ private boolean onTouchUp(int id, float x, float y, float pressure) { return false; } - private int notesPressed() - { - int res = 0; - for (int i = 0; i < FINGERS; i++) { - if (noteForFinger_[i] != -1) - res++; - } - return res; - } - - private boolean handleScrollTouch(int id, float x) - { - if (touchDragAction_ == TouchDragAction.TDA_ScrollKeyboard) - { - if (noteForFinger_[id] != -1) - { - /* - The effective scroll offset is the current touch devided by the number of current active touches - This correspondes to the following cases: - - A single touch: should scroll exactly the same amount as the touch moved, no perceived mismatch between expectations and reality - - Multi-touch: either the player moves the fingers more or less synchronously or some of them moves more or less comparing to the others - The latter might introduce the sense that the scrolling moves too much or too little because of the mathematics described. - */ - touchTrackingOffset = touchTrackingOffset + (x - touchCurrentX[id]) / notesPressed(); - - touchCurrentX[id] = x; - - if (Math.abs(touchTrackingOffset - offset_) >= 1) - { - setScrollZoom(touchTrackingOffset, zoom_); - } - } - return true; - } else - return false; - } - private boolean onTouchMove(int id, float x, float y, float pressure) { if (handleScrollTouch(id, x)) { @@ -357,6 +320,43 @@ private static String noteString(int note) { return NOTE_NAMES[note % 12] + Integer.toString(octave); } + private int notesPressed() + { + int res = 0; + for (int i = 0; i < FINGERS; i++) { + if (noteForFinger_[i] != -1) + res++; + } + return res; + } + + private boolean handleScrollTouch(int id, float x) + { + if (touchDragAction_ == TouchDragAction.TDA_ScrollKeyboard) + { + if (noteForFinger_[id] != -1) + { + /* + The effective scroll offset is the current touch devided by the number of current active touches + This correspondes to the following cases: + - A single touch: should scroll exactly the same amount as the touch moved, no perceived mismatch between expectations and reality + - Multi-touch: either the player moves the fingers more or less synchronously or some of them moves more or less comparing to the others + The latter might introduce the sense that the scrolling moves too much or too little because of the mathematics described. + */ + touchTrackingOffset = touchTrackingOffset + (x - touchCurrentX[id]) / notesPressed(); + + touchCurrentX[id] = x; + + if (Math.abs(touchTrackingOffset - offset_) >= 1) + { + setScrollZoom(touchTrackingOffset, zoom_); + } + } + return true; + } else + return false; + } + private static final String TAG = "KEYBWIDGET";