diff --git a/android/.externalToolBuilders/NDK Builder.launch b/android/.externalToolBuilders/NDK Builder.launch
new file mode 100644
index 0000000..0f8f837
--- /dev/null
+++ b/android/.externalToolBuilders/NDK Builder.launch
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/android/jni/Android.mk b/android/jni/Android.mk
new file mode 100644
index 0000000..249e1df
--- /dev/null
+++ b/android/jni/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH := $(call my-dir)/../../cpp/src
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := synth
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_SRC_FILES := dx7note.cc \
+ env.cc \
+ fm_core.cc \
+ fm_op_kernel.cc \
+ freqlut.cc \
+ resofilter.cc \
+ ringbuffer.cc \
+ sawtooth.cc \
+ sin.cc \
+ synth_unit.cc
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/android/jni/Application.mk b/android/jni/Application.mk
new file mode 100644
index 0000000..caf3b26
--- /dev/null
+++ b/android/jni/Application.mk
@@ -0,0 +1 @@
+APP_STL := stlport_static
diff --git a/android/project.properties b/android/project.properties
new file mode 100644
index 0000000..0840b4a
--- /dev/null
+++ b/android/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-15
diff --git a/android/src/com/google/synthesizer/android/AndroidGlue.java b/android/src/com/google/synthesizer/android/AndroidGlue.java
new file mode 100644
index 0000000..165d539
--- /dev/null
+++ b/android/src/com/google/synthesizer/android/AndroidGlue.java
@@ -0,0 +1,14 @@
+package com.google.synthesizer.android;
+
+
+public class AndroidGlue {
+
+ public native void start();
+ public native void setPlayState(boolean isPlaying);
+ public native void sendMidi(byte[] midiData);
+
+ static {
+ System.loadLibrary("synth");
+ }
+}
+
diff --git a/android/src/com/google/synthesizer/android/ui/PianoActivity2.java b/android/src/com/google/synthesizer/android/ui/PianoActivity2.java
new file mode 100644
index 0000000..0ec8fd0
--- /dev/null
+++ b/android/src/com/google/synthesizer/android/ui/PianoActivity2.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.synthesizer.android.ui;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.Spinner;
+
+import com.google.synthesizer.R;
+import com.google.synthesizer.android.AndroidGlue;
+import com.google.synthesizer.android.widgets.knob.KnobView;
+import com.google.synthesizer.android.widgets.piano.PianoView;
+
+/**
+ * Activity for simply playing the piano.
+ * This version is hacked up to send MIDI to the C++ engine. This needs to
+ * be refactored to make it cleaner.
+ */
+public class PianoActivity2 extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.piano);
+
+ piano_ = (PianoView)findViewById(R.id.piano);
+ volumeKnob_ = (KnobView)findViewById(R.id.volumeKnob);
+ presetSpinner_ = (Spinner)findViewById(R.id.presetSpinner);
+ // TODO: wire these up (preset spinner should send patch selection)
+
+ androidGlue_ = new AndroidGlue();
+ androidGlue_.start();
+
+ piano_.bindTo(androidGlue_);
+ }
+
+ @Override
+ protected void onPause() {
+ androidGlue_.setPlayState(false);
+ super.onPause();
+ }
+
+ @Override
+ protected void onResume() {
+ androidGlue_.setPlayState(true);
+ super.onResume();
+ }
+
+ private AndroidGlue androidGlue_;
+ private PianoView piano_;
+ private KnobView volumeKnob_;
+ private Spinner presetSpinner_;
+}
diff --git a/cpp/src/android_glue.cc b/cpp/src/android_glue.cc
new file mode 100644
index 0000000..a1e4505
--- /dev/null
+++ b/cpp/src/android_glue.cc
@@ -0,0 +1,145 @@
+#include
+#include
+#include
+#include
+#include
+
+#include
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "synth", __VA_ARGS__)
+
+#include "synth.h"
+#include "freqlut.h"
+#include "sin.h"
+#include "synth_unit.h"
+
+RingBuffer *ring_buffer;
+SynthUnit *synth_unit;
+
+const int N_BUFFERS = 2;
+const int BUFFER_SIZE = 64;
+
+int16_t buffer[BUFFER_SIZE * N_BUFFERS];
+int cur_buffer = 0;
+int count = 0;
+
+// engine interfaces
+static SLObjectItf engineObject = NULL;
+static SLEngineItf engineEngine;
+
+// output mix interfaces
+static SLObjectItf outputMixObject = NULL;
+
+// buffer queue player interfaces
+static SLObjectItf bqPlayerObject = NULL;
+static SLPlayItf bq_player_play;
+static SLVolumeItf bq_player_volume;
+static SLAndroidSimpleBufferQueueItf bq_player_buffer_queue;
+static SLBufferQueueItf buffer_queue_itf;
+
+extern "C" void BqPlayerCallback(SLAndroidSimpleBufferQueueItf queueItf,
+ void *data) {
+ if (count >= 1000) return;
+ int16_t *buf_ptr = buffer + BUFFER_SIZE * cur_buffer;
+ synth_unit->GetSamples(BUFFER_SIZE, buf_ptr);
+ SLresult result = (*queueItf)->Enqueue(bq_player_buffer_queue,
+ buf_ptr, BUFFER_SIZE * 2);
+ assert(SL_RESULT_SUCCESS == result);
+ cur_buffer = (cur_buffer + 1) % N_BUFFERS;
+}
+
+extern "C" JNIEXPORT jint JNICALL
+Java_com_google_synthesizer_android_AndroidGlue_hello(JNIEnv *env,
+ jobject thiz) {
+ LOGI("here %d!", 42);
+ return 42;
+}
+
+void CreateEngine() {
+ SLresult result;
+ result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
+ assert(SL_RESULT_SUCCESS == result);
+
+ result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
+ assert(SL_RESULT_SUCCESS == result);
+
+ result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE,
+ &engineEngine);
+ assert(SL_RESULT_SUCCESS == result);
+
+ result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject,
+ 0, NULL, NULL);
+ assert(SL_RESULT_SUCCESS == result);
+ result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
+ assert(SL_RESULT_SUCCESS == result);
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_synthesizer_android_AndroidGlue_start(JNIEnv *env,
+ jobject thiz) {
+ CreateEngine();
+ SLDataLocator_AndroidSimpleBufferQueue loc_bufq =
+ {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, N_BUFFERS};
+ SLDataFormat_PCM format_pcm = {
+ SL_DATAFORMAT_PCM, 1, SL_SAMPLINGRATE_48,
+ SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
+ SL_SPEAKER_FRONT_CENTER, SL_BYTEORDER_LITTLEENDIAN
+ // TODO: compute real endianness
+ };
+ SLDataSource audio_src = {&loc_bufq, &format_pcm};
+ SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX,
+ outputMixObject};
+ SLDataSink audio_sink = {&loc_outmix, NULL};
+ const SLInterfaceID ids[2] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME};
+ const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
+ SLresult result;
+ result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject,
+ &audio_src, &audio_sink, 2, ids, req);
+ assert(SL_RESULT_SUCCESS == result);
+ result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
+ assert(SL_RESULT_SUCCESS == result);
+ result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY,
+ &bq_player_play);
+ assert(SL_RESULT_SUCCESS == result);
+ result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,
+ &bq_player_buffer_queue);
+ assert(SL_RESULT_SUCCESS == result);
+
+ result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME,
+ &bq_player_volume);
+ assert(SL_RESULT_SUCCESS == result);
+ result = (*bq_player_buffer_queue)->RegisterCallback(bq_player_buffer_queue,
+ &BqPlayerCallback, NULL);
+ assert(SL_RESULT_SUCCESS == result);
+
+ double sample_rate = 48000.0;
+ Freqlut::init(sample_rate);
+ Sin::init();
+ ring_buffer = new RingBuffer();
+ synth_unit = new SynthUnit(ring_buffer);
+ for (int i = 0; i < N_BUFFERS - 1; ++i) {
+ BqPlayerCallback(bq_player_buffer_queue, NULL);
+ }
+
+ result = (*bq_player_play)->SetPlayState(bq_player_play,
+ SL_PLAYSTATE_PLAYING);
+ assert(SL_RESULT_SUCCESS == result);
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_synthesizer_android_AndroidGlue_sendMidi(JNIEnv *env,
+ jobject thiz, jbyteArray jb) {
+ uint8_t *data = (uint8_t *)env->GetByteArrayElements(jb, NULL);
+ if (data != NULL) {
+ ring_buffer->Write(data, env->GetArrayLength(jb));
+ env->ReleaseByteArrayElements(jb, (jbyte *)data, JNI_ABORT);
+ }
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_synthesizer_android_AndroidGlue_setPlayState(JNIEnv *env,
+ jobject thiz, jboolean isPlaying) {
+ SLresult result = (*bq_player_play)->SetPlayState(bq_player_play,
+ isPlaying ? SL_PLAYSTATE_PLAYING : SL_PLAYSTATE_PAUSED);
+ assert(SL_RESULT_SUCCESS == result);
+}
+
diff --git a/cpp/src/synth.h b/cpp/src/synth.h
index f5aca84..8074ac6 100644
--- a/cpp/src/synth.h
+++ b/cpp/src/synth.h
@@ -29,14 +29,15 @@
#define SynthMemoryBarrier() OSMemoryBarrier()
#elif defined(__GNUC__)
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)
-#define SynthMemoryBarrier __sync_synchronize()
-#else
-#warning Memory barrier is not enabled
+#define SynthMemoryBarrier() __sync_synchronize()
#endif
-#warning Memory barrier is not enabled
#endif
// #undef SynthMemoryBarrier()
-// #define SynthMemoryBarrier()
+
+#ifndef SynthMemoryBarrier
+#warning Memory barrier is not enabled
+#define SynthMemoryBarrier()
+#endif
#endif // __SYNTH_H
diff --git a/cpp/src/synth_unit.cc b/cpp/src/synth_unit.cc
index 406dd12..448c66a 100644
--- a/cpp/src/synth_unit.cc
+++ b/cpp/src/synth_unit.cc
@@ -39,6 +39,7 @@ SynthUnit::SynthUnit(RingBuffer *ring_buffer) {
input_buffer_index_ = 0;
std::memcpy(patch_data_, epiano, sizeof(epiano));
current_patch_ = 0;
+ current_note_ = 0;
}
// Transfer as many bytes as possible from ring buffer to input buffer.