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