diff --git a/android/res/layout/piano2.xml b/android/res/layout/piano2.xml
new file mode 100644
index 0000000..ffb7b36
--- /dev/null
+++ b/android/res/layout/piano2.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml
index fb495e3..c087a5a 100644
--- a/android/res/values/strings.xml
+++ b/android/res/values/strings.xml
@@ -47,6 +47,7 @@
Cutoff
+ Resonance
Attack
@@ -76,4 +77,4 @@
- Amplification
- Effects
-
\ No newline at end of file
+
diff --git a/android/src/com/google/synthesizer/android/ui/PianoActivity2.java b/android/src/com/google/synthesizer/android/ui/PianoActivity2.java
index be1b053..efb66db 100644
--- a/android/src/com/google/synthesizer/android/ui/PianoActivity2.java
+++ b/android/src/com/google/synthesizer/android/ui/PianoActivity2.java
@@ -39,10 +39,10 @@ import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;
-import android.widget.ToggleButton;
import com.google.synthesizer.R;
import com.google.synthesizer.android.AndroidGlue;
+import com.google.synthesizer.android.widgets.knob.KnobListener;
import com.google.synthesizer.android.widgets.knob.KnobView;
import com.google.synthesizer.android.widgets.piano.PianoView;
@@ -57,10 +57,11 @@ public class PianoActivity2 extends Activity {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN |
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- setContentView(R.layout.piano);
+ setContentView(R.layout.piano2);
piano_ = (PianoView)findViewById(R.id.piano);
- volumeKnob_ = (KnobView)findViewById(R.id.volumeKnob);
+ cutoffKnob_ = (KnobView)findViewById(R.id.cutoffKnob);
+ resonanceKnob_ = (KnobView)findViewById(R.id.resonanceKnob);
presetSpinner_ = (Spinner)findViewById(R.id.presetSpinner);
androidGlue_ = new AndroidGlue();
@@ -89,6 +90,20 @@ public class PianoActivity2 extends Activity {
public void onNothingSelected(AdapterView> parent) {
}
});
+
+ cutoffKnob_.setKnobListener(new KnobListener() {
+ public void onKnobChanged(double newValue) {
+ int value = (int)Math.round(newValue * 127);
+ androidGlue_.onController(0, 1, value);
+ }
+ });
+ resonanceKnob_.setKnobListener(new KnobListener() {
+ public void onKnobChanged(double newValue) {
+ int value = (int)Math.round(newValue * 127);
+ androidGlue_.onController(0, 2, value);
+ }
+ });
+
piano_.bindTo(androidGlue_);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
tryConnectUsb();
@@ -134,8 +149,8 @@ public class PianoActivity2 extends Activity {
for (int i = 0; i < nBytes; i += 4) {
int codeIndexNumber = buf[i] & 0xf;
int payloadBytes = 0;
- if (codeIndexNumber == 8 || codeIndexNumber == 9) {
- // TODO: pitchbend, control, etc
+ if (codeIndexNumber == 8 || codeIndexNumber == 9 || codeIndexNumber == 11 ||
+ codeIndexNumber == 14) {
payloadBytes = 3;
} else if (codeIndexNumber == 12) {
payloadBytes = 2;
@@ -173,6 +188,7 @@ public class PianoActivity2 extends Activity {
private AndroidGlue androidGlue_;
private PianoView piano_;
- private KnobView volumeKnob_;
+ private KnobView cutoffKnob_;
+ private KnobView resonanceKnob_;
private Spinner presetSpinner_;
}
diff --git a/android/src/com/google/synthesizer/android/widgets/knob/KnobView.java b/android/src/com/google/synthesizer/android/widgets/knob/KnobView.java
index f908174..60f31a9 100644
--- a/android/src/com/google/synthesizer/android/widgets/knob/KnobView.java
+++ b/android/src/com/google/synthesizer/android/widgets/knob/KnobView.java
@@ -74,7 +74,7 @@ public class KnobView extends View {
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
- case MotionEvent.ACTION_POINTER_DOWN: {
+ case MotionEvent.ACTION_DOWN: {
// Just record the current finger position.
getDrawingRect(rect_);
previousX_ = event.getX() - rect_.centerX();
@@ -116,7 +116,7 @@ public class KnobView extends View {
break;
}
- case MotionEvent.ACTION_POINTER_UP: {
+ case MotionEvent.ACTION_UP: {
break;
}
}
diff --git a/cpp/src/module.h b/cpp/src/module.h
index 2d7624c..e165531 100644
--- a/cpp/src/module.h
+++ b/cpp/src/module.h
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#ifndef SYNTH_MODULE_H
+#define SYNTH_MODULE_H
+
#include
class Module {
@@ -23,3 +26,6 @@ class Module {
virtual void process(const int32_t **inbufs, const int32_t *control_in,
const int32_t *control_last, int32_t **outbufs) = 0;
};
+
+#endif // SYNTH_MODULE_H
+
diff --git a/cpp/src/resofilter.cc b/cpp/src/resofilter.cc
index 8a6f861..dc33d5d 100644
--- a/cpp/src/resofilter.cc
+++ b/cpp/src/resofilter.cc
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-#include "module.h"
+#include "synth.h"
+#include "freqlut.h"
#include "resofilter.h"
double this_sample_rate;
@@ -30,8 +31,8 @@ ResoFilter::ResoFilter() {
}
int32_t compute_alpha(int32_t logf) {
- // TODO
- return 1 << 21;
+ // TODO: better tuning
+ return min(1 << 24, Freqlut::lookup(logf));
}
void ResoFilter::process(const int32_t **inbufs, const int32_t *control_in,
@@ -42,6 +43,12 @@ void ResoFilter::process(const int32_t **inbufs, const int32_t *control_in,
int32_t k = control_last[1];
int32_t k_in = control_in[1];
int32_t delta_k = (k_in - k) >> lg_n;
+ if ((((int64_t)alpha_in * (int64_t)k_in) >> 24) > 1 << 24) {
+ k_in = ((1 << 30) / alpha_in) << 18;
+ }
+ if ((((int64_t)alpha * (int64_t)k) >> 24) > 1 << 24) {
+ k = ((1 << 30) / alpha) << 18;
+ }
const int32_t *ibuf = inbufs[0];
int32_t *obuf = outbufs[0];
int x0 = x[0];
diff --git a/cpp/src/resofilter.h b/cpp/src/resofilter.h
index f6437e0..3937047 100644
--- a/cpp/src/resofilter.h
+++ b/cpp/src/resofilter.h
@@ -14,6 +14,11 @@
* limitations under the License.
*/
+#ifndef SYNTH_RESOFILTER_H_
+#define SYNTH_RESOFILTER_H_
+
+#include "module.h"
+
class ResoFilter : Module {
public:
ResoFilter();
@@ -25,3 +30,5 @@ class ResoFilter : Module {
private:
int32_t x[4];
};
+
+#endif // SYNTH_RESOFILTER_H_
diff --git a/cpp/src/synth_unit.cc b/cpp/src/synth_unit.cc
index 6121167..41a58e7 100644
--- a/cpp/src/synth_unit.cc
+++ b/cpp/src/synth_unit.cc
@@ -44,6 +44,8 @@ SynthUnit::SynthUnit(RingBuffer *ring_buffer) {
memcpy(patch_data_, epiano, sizeof(epiano));
current_patch_ = 0;
current_note_ = 0;
+ filter_control_[0] = 258847126;
+ filter_control_[1] = 0;
}
// Transfer as many bytes as possible from ring buffer to input buffer.
@@ -97,6 +99,17 @@ int SynthUnit::ProcessMidiMessage(const uint8_t *buf, int buf_size) {
return 3;
}
return 0;
+ } else if (cmd_type == 0xb0) {
+ if (buf_size >= 3) {
+ int controller = buf[1];
+ int value = buf[2];
+ if (controller == 1) {
+ filter_control_[0] = 129423563 + value * 1019083;
+ } else if (controller == 2) {
+ filter_control_[1] = value * 528416;
+ }
+ return 3;
+ } return 0;
} else if (cmd_type == 0xc0) {
if (buf_size >= 2) {
// program change
@@ -149,6 +162,7 @@ void SynthUnit::GetSamples(int n_samples, int16_t *buffer) {
for (int i = 0; i < n_samples; i += N) {
int32_t audiobuf[N];
+ int32_t audiobuf2[N];
for (int j = 0; j < N; ++j) {
audiobuf[j] = 0;
}
@@ -157,8 +171,11 @@ void SynthUnit::GetSamples(int n_samples, int16_t *buffer) {
active_note_[note].dx7_note->compute(audiobuf);
}
}
+ const int32_t *bufs[] = { audiobuf };
+ int32_t *bufs2[] = { audiobuf2 };
+ filter_.process(bufs, filter_control_, filter_control_, bufs2);
for (int j = 0; j < N; ++j) {
- int32_t val = audiobuf[j] >> 4;
+ int32_t val = audiobuf2[j] >> 4;
int clip_val = val < -(1 << 24) ? 0x8000 : val >= (1 << 24) ? 0x7fff :
val >> 9;
// TODO: maybe some dithering?
diff --git a/cpp/src/synth_unit.h b/cpp/src/synth_unit.h
index 5ad59c0..51fdae9 100644
--- a/cpp/src/synth_unit.h
+++ b/cpp/src/synth_unit.h
@@ -16,6 +16,7 @@
#include "dx7note.h"
#include "ringbuffer.h"
+#include "resofilter.h"
struct ActiveNote {
int midi_note;
@@ -35,7 +36,7 @@ class SynthUnit {
int ProcessMidiMessage(const uint8_t *buf, int buf_size);
RingBuffer *ring_buffer_;
- static const int max_active_notes = 16;
+ static const int max_active_notes = 8;
ActiveNote active_note_[max_active_notes];
int current_note_;
uint8_t input_buffer_[8192];
@@ -43,4 +44,7 @@ class SynthUnit {
uint8_t patch_data_[4096];
int current_patch_;
+
+ ResoFilter filter_;
+ int32_t filter_control_[2];
};