diff --git a/android/res/layout/piano2.xml b/android/res/layout/piano2.xml
index ffb7b36..8ffbad7 100644
--- a/android/res/layout/piano2.xml
+++ b/android/res/layout/piano2.xml
@@ -46,11 +46,21 @@
app:min="0"
app:max="1"
android:layout_margin="2px" />
+
+ />
+
+
= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
tryConnectUsb();
}
+
+ statusHandler_ = new Handler();
+ statusRunnable_ = new Runnable() {
+ public void run() {
+ int n = androidGlue_.statsBytesAvailable();
+ if (n > 0) {
+ byte[] buf = new byte[n];
+ androidGlue_.readStatsBytes(buf, 0, n);
+ TextView statusTextView = (TextView)findViewById(R.id.status);
+ String statusString = new String(buf);
+ int nlIndex = statusString.indexOf('\n');
+ if (nlIndex >= 0) {
+ statusString = statusString.substring(0, nlIndex);
+ }
+ statusTextView.setText(statusString);
+ }
+ statusHandler_.postDelayed(statusRunnable_, 10);
+ }
+ };
+ statusRunnable_.run();
}
@Override
@@ -228,4 +250,6 @@ public class PianoActivity2 extends Activity {
private KnobView cutoffKnob_;
private KnobView resonanceKnob_;
private Spinner presetSpinner_;
+ private Handler statusHandler_;
+ private Runnable statusRunnable_;
}
diff --git a/cpp/src/android_glue.cc b/cpp/src/android_glue.cc
index 0e647ee..0a3781f 100644
--- a/cpp/src/android_glue.cc
+++ b/cpp/src/android_glue.cc
@@ -16,6 +16,8 @@
#include
#include
+#include
+#include
#include
#include
#include
@@ -29,6 +31,7 @@
#include "synth_unit.h"
RingBuffer *ring_buffer;
+RingBuffer *stats_ring_buffer;
SynthUnit *synth_unit;
const int N_BUFFERS = 2;
@@ -37,7 +40,6 @@ int buffer_size;
int16_t buffer[MAX_BUFFER_SIZE * N_BUFFERS];
int cur_buffer = 0;
-int count = 0;
// engine interfaces
static SLObjectItf engineObject = NULL;
@@ -52,15 +54,30 @@ static SLPlayItf bq_player_play;
static SLAndroidSimpleBufferQueueItf bq_player_buffer_queue;
static SLBufferQueueItf buffer_queue_itf;
+double ts_to_double(const struct timespec *tp) {
+ return tp->tv_sec + 1e-9 * tp->tv_nsec;
+}
+
extern "C" void BqPlayerCallback(SLAndroidSimpleBufferQueueItf queueItf,
void *data) {
- if (count >= 1000) return;
+ struct timespec tp;
+ clock_gettime(CLOCK_MONOTONIC, &tp);
+ double start_time = ts_to_double(&tp);
int16_t *buf_ptr = buffer + buffer_size * cur_buffer;
synth_unit->GetSamples(buffer_size, buf_ptr);
+ clock_gettime(CLOCK_MONOTONIC, &tp);
+ double end_time = ts_to_double(&tp);
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;
+ char buf[64];
+ int n = sprintf(buf, "ts %.6f %.6f\n", start_time, end_time);
+ if (n <= stats_ring_buffer->WriteBytesAvailable()) {
+ stats_ring_buffer->Write((const uint8_t *)buf, n);
+ }
+ // Could potentially defer writing indication of overrun, but probably
+ // not worth it.
}
void CreateEngine() {
@@ -122,6 +139,7 @@ Java_com_google_synthesizer_android_AndroidGlue_start(JNIEnv *env,
Freqlut::init(sample_rate);
Sin::init();
ring_buffer = new RingBuffer();
+ stats_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);
@@ -153,6 +171,8 @@ Java_com_google_synthesizer_android_AndroidGlue_shutdown(JNIEnv *env,
}
delete ring_buffer;
ring_buffer = NULL;
+ delete stats_ring_buffer;
+ stats_ring_buffer = NULL;
delete synth_unit;
synth_unit = NULL;
}
@@ -175,3 +195,28 @@ Java_com_google_synthesizer_android_AndroidGlue_setPlayState(JNIEnv *env,
assert(SL_RESULT_SUCCESS == result);
}
+extern "C" JNIEXPORT jint JNICALL
+Java_com_google_synthesizer_android_AndroidGlue_statsBytesAvailable(
+ JNIEnv *env, jobject thiz) {
+ return stats_ring_buffer->BytesAvailable();
+}
+
+extern "C" JNIEXPORT jint JNICALL
+Java_com_google_synthesizer_android_AndroidGlue_readStatsBytes(
+ JNIEnv *env, jobject thiz, jbyteArray jb, jint off, jint len) {
+ int bytes_available = stats_ring_buffer->BytesAvailable();
+ int n = min(bytes_available, len);
+ if (n) {
+ size_t uoff = off;
+ size_t ulen = len;
+ if (off >= 0 && len >= 0 && uoff + ulen <= env->GetArrayLength(jb)) {
+ uint8_t *buf = (uint8_t *)env->GetByteArrayElements(jb, NULL);
+ stats_ring_buffer->Read(n, buf + uoff);
+ env->ReleaseByteArrayElements(jb, (jbyte *)buf, 0);
+ } else {
+ env->ThrowNew(env->FindClass("java/lang/ArrayIndexOutOfBoundsException"),
+ "out of bounds in AndroidGlue.readStatsBytes");
+ }
+ }
+ return n;
+}
\ No newline at end of file
diff --git a/cpp/src/ringbuffer.cc b/cpp/src/ringbuffer.cc
index e0b1328..a59361e 100644
--- a/cpp/src/ringbuffer.cc
+++ b/cpp/src/ringbuffer.cc
@@ -29,6 +29,10 @@ int RingBuffer::BytesAvailable() {
return (wr_ix_ - rd_ix_) & (kBufSize - 1);
}
+int RingBuffer::WriteBytesAvailable() {
+ return (rd_ix_ - wr_ix_ - 1) & (kBufSize - 1);
+}
+
int RingBuffer::Read(int size, uint8_t *bytes) {
int rd_ix = rd_ix_;
SynthMemoryBarrier(); // read barrier, make sure data is committed before ix
diff --git a/cpp/src/ringbuffer.h b/cpp/src/ringbuffer.h
index 250a590..c8c3e72 100644
--- a/cpp/src/ringbuffer.h
+++ b/cpp/src/ringbuffer.h
@@ -24,6 +24,9 @@ class RingBuffer {
// Returns number of bytes available for reading.
int BytesAvailable();
+ // Returns number of bytes that can be written without blocking.
+ int WriteBytesAvailable();
+
// Reads bytes. It is the caller's responsibility to make sure that
// size <= a previous value of BytesAvailable().
int Read(int size, uint8_t *bytes);
@@ -34,8 +37,8 @@ class RingBuffer {
private:
static const int kBufSize = 8192;
uint8_t buf_[kBufSize];
- volatile int rd_ix_;
- volatile int wr_ix_;
+ volatile unsigned int rd_ix_;
+ volatile unsigned int wr_ix_;
};
#endif // SYNTH_RINGBUFFER_H_