diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml
index b8757cf..7110f1b 100644
--- a/android/AndroidManifest.xml
+++ b/android/AndroidManifest.xml
@@ -5,7 +5,7 @@
android:versionCode="2"
android:versionName="0.9">
diff --git a/android/project.properties b/android/project.properties
index 8937e94..a3ee5ab 100644
--- a/android/project.properties
+++ b/android/project.properties
@@ -11,4 +11,4 @@
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
-target=android-14
+target=android-17
diff --git a/android/src/com/google/synthesizer/android/AndroidGlue.java b/android/src/com/google/synthesizer/android/AndroidGlue.java
index 9a86a64..5e33c51 100644
--- a/android/src/com/google/synthesizer/android/AndroidGlue.java
+++ b/android/src/com/google/synthesizer/android/AndroidGlue.java
@@ -14,7 +14,7 @@ public class AndroidGlue extends MessageOutputProcessor {
/**
* Create and initialize the engine. This should be done once per process.
*/
- public native void start();
+ public native void start(int sample_rate, int buf_size);
/**
* Shut down the OpenSL ES engine and audio synthesizer.
diff --git a/android/src/com/google/synthesizer/android/ui/PianoActivity2.java b/android/src/com/google/synthesizer/android/ui/PianoActivity2.java
index efb66db..9173159 100644
--- a/android/src/com/google/synthesizer/android/ui/PianoActivity2.java
+++ b/android/src/com/google/synthesizer/android/ui/PianoActivity2.java
@@ -21,6 +21,7 @@ import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
+import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.hardware.usb.UsbConstants;
@@ -29,6 +30,7 @@ import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
+import android.media.AudioManager;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
@@ -64,8 +66,15 @@ public class PianoActivity2 extends Activity {
resonanceKnob_ = (KnobView)findViewById(R.id.resonanceKnob);
presetSpinner_ = (Spinner)findViewById(R.id.presetSpinner);
+ AudioParams params = new AudioParams(44100, 384);
+ // TODO: for pre-JB-MR1 devices, do some matching against known devices to
+ // get best audio parameters.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ getJbMr1Params(params);
+ }
+
androidGlue_ = new AndroidGlue();
- androidGlue_.start();
+ androidGlue_.start(params.sampleRate, params.bufferSize);
InputStream patchIs = getResources().openRawResource(R.raw.rom1a);
byte[] patchData = new byte[4104];
@@ -128,6 +137,7 @@ public class PianoActivity2 extends Activity {
super.onResume();
}
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
private UsbEndpoint getInputEndpoint(UsbInterface usbIf) {
int nEndpoints = usbIf.getEndpointCount();
for (int i = 0; i < nEndpoints; i++) {
@@ -140,6 +150,7 @@ public class PianoActivity2 extends Activity {
return null;
}
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
private void startUsbThread(final UsbDeviceConnection connection, final UsbEndpoint endpoint) {
Thread thread = new Thread(new Runnable() {
public void run() {
@@ -166,6 +177,7 @@ public class PianoActivity2 extends Activity {
});
thread.start();
}
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
private void tryConnectUsb() {
UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
HashMap deviceList = usbManager.getDeviceList();
@@ -185,7 +197,32 @@ public class PianoActivity2 extends Activity {
}
}
}
-
+
+ class AudioParams {
+ AudioParams(int sr, int bs) {
+ confident = false;
+ sampleRate = sr;
+ bufferSize = bs;
+ }
+ public String toString() {
+ return "sampleRate=" + sampleRate + " bufferSize=" + bufferSize;
+ }
+ boolean confident;
+ int sampleRate;
+ int bufferSize;
+ }
+
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
+ void getJbMr1Params(AudioParams params) {
+ AudioManager audioManager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
+ String sr = audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
+ String bs = audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
+ params.confident = true;
+ params.sampleRate = Integer.parseInt(sr);
+ params.bufferSize = Integer.parseInt(bs);
+ //log("from platform: " + params);
+ }
+
private AndroidGlue androidGlue_;
private PianoView piano_;
private KnobView cutoffKnob_;
diff --git a/cpp/src/android_glue.cc b/cpp/src/android_glue.cc
index 8f6604b..0e647ee 100644
--- a/cpp/src/android_glue.cc
+++ b/cpp/src/android_glue.cc
@@ -32,9 +32,10 @@ RingBuffer *ring_buffer;
SynthUnit *synth_unit;
const int N_BUFFERS = 2;
-const int BUFFER_SIZE = 384;
+const int MAX_BUFFER_SIZE = 1024;
+int buffer_size;
-int16_t buffer[BUFFER_SIZE * N_BUFFERS];
+int16_t buffer[MAX_BUFFER_SIZE * N_BUFFERS];
int cur_buffer = 0;
int count = 0;
@@ -54,10 +55,10 @@ 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);
+ 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);
+ buf_ptr, buffer_size * 2);
assert(SL_RESULT_SUCCESS == result);
cur_buffer = (cur_buffer + 1) % N_BUFFERS;
}
@@ -84,12 +85,12 @@ SLresult result;
extern "C" JNIEXPORT void JNICALL
Java_com_google_synthesizer_android_AndroidGlue_start(JNIEnv *env,
- jobject thiz) {
+ jobject thiz, jint sample_rate, jint buf_size) {
CreateEngine();
SLDataLocator_AndroidSimpleBufferQueue loc_bufq =
{SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, N_BUFFERS};
SLDataFormat_PCM format_pcm = {
- SL_DATAFORMAT_PCM, 1, SL_SAMPLINGRATE_44_1,
+ SL_DATAFORMAT_PCM, 1, sample_rate * 1000,
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
SL_SPEAKER_FRONT_CENTER, SL_BYTEORDER_LITTLEENDIAN
// TODO: compute real endianness
@@ -117,7 +118,7 @@ Java_com_google_synthesizer_android_AndroidGlue_start(JNIEnv *env,
&BqPlayerCallback, NULL);
assert(SL_RESULT_SUCCESS == result);
- double sample_rate = 44100.0;
+ buffer_size = buf_size;
Freqlut::init(sample_rate);
Sin::init();
ring_buffer = new RingBuffer();
diff --git a/cpp/src/synth_unit.cc b/cpp/src/synth_unit.cc
index 63a59e2..f024d0a 100644
--- a/cpp/src/synth_unit.cc
+++ b/cpp/src/synth_unit.cc
@@ -18,6 +18,11 @@
#include
#endif
+#ifdef __ANDROID__
+#include
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "synth", __VA_ARGS__)
+#endif
+
#include
#include "synth.h"
@@ -50,6 +55,7 @@ SynthUnit::SynthUnit(RingBuffer *ring_buffer) {
filter_control_[0] = 258847126;
filter_control_[1] = 0;
sustain_ = false;
+ extra_buf_size_ = 0;
}
// Transfer as many bytes as possible from ring buffer to input buffer.
@@ -193,7 +199,19 @@ void SynthUnit::GetSamples(int n_samples, int16_t *buffer) {
}
ConsumeInput(input_offset);
- for (int i = 0; i < n_samples; i += N) {
+ int i;
+ for (i = 0; i < n_samples && i < extra_buf_size_; i++) {
+ buffer[i] = extra_buf_[i];
+ }
+ if (extra_buf_size_ > n_samples) {
+ for (int j = 0; j < extra_buf_size_ - n_samples; j++) {
+ extra_buf_[j] = extra_buf_[j + n_samples];
+ }
+ extra_buf_size_ -= n_samples;
+ return;
+ }
+
+ for (; i < n_samples; i += N) {
AlignedBuf audiobuf;
int32_t audiobuf2[N];
for (int j = 0; j < N; ++j) {
@@ -207,12 +225,18 @@ void SynthUnit::GetSamples(int n_samples, int16_t *buffer) {
const int32_t *bufs[] = { audiobuf.get() };
int32_t *bufs2[] = { audiobuf2 };
filter_.process(bufs, filter_control_, filter_control_, bufs2);
+ int jmax = n_samples - i;
for (int j = 0; j < N; ++j) {
int32_t val = audiobuf2[j] >> 4;
int clip_val = val < -(1 << 24) ? 0x8000 : val >= (1 << 24) ? 0x7fff :
val >> 9;
// TODO: maybe some dithering?
- buffer[i + j] = clip_val;
+ if (j < jmax) {
+ buffer[i + j] = clip_val;
+ } else {
+ extra_buf_[j - jmax] = clip_val;
+ }
}
}
+ extra_buf_size_ = i - n_samples;
}
diff --git a/cpp/src/synth_unit.h b/cpp/src/synth_unit.h
index fdcd3ed..31cea4f 100644
--- a/cpp/src/synth_unit.h
+++ b/cpp/src/synth_unit.h
@@ -42,7 +42,7 @@ class SynthUnit {
int ProcessMidiMessage(const uint8_t *buf, int buf_size);
RingBuffer *ring_buffer_;
- static const int max_active_notes = 8;
+ static const int max_active_notes = 16;
ActiveNote active_note_[max_active_notes];
int current_note_;
uint8_t input_buffer_[8192];
@@ -54,4 +54,8 @@ class SynthUnit {
ResoFilter filter_;
int32_t filter_control_[2];
bool sustain_;
+
+ // Extra buffering for when GetSamples wants a buffer not a multiple of N
+ int16_t extra_buf_[N];
+ int extra_buf_size_;
};