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]; };