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..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
@@ -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,51 @@ 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);
+
+ 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;
}
- noteForFinger_[id] = newNote;
- noteStatus_[newNote] = (byte)velocity;
+ return true;
}
- return true;
+
+ return false;
}
- return false;
}
private static String noteString(int note) {
@@ -276,6 +320,46 @@ 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";
+
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" />
+
+
+