Adding the glue logic (and an activity for testing) to run the C++ sound

engine on Android. Note that this particular patchset disables the Java-based
synthesis, so probably shouldn't be committed as-is to mainline.
bklimt
Raph Levien 13 years ago
parent a99ac7a38c
commit c3509e50c1
  1. 10
      android/.externalToolBuilders/NDK Builder.launch
  2. 18
      android/jni/Android.mk
  3. 1
      android/jni/Application.mk
  4. 14
      android/project.properties
  5. 14
      android/src/com/google/synthesizer/android/AndroidGlue.java
  6. 66
      android/src/com/google/synthesizer/android/ui/PianoActivity2.java
  7. 145
      cpp/src/android_glue.cc
  8. 11
      cpp/src/synth.h
  9. 1
      cpp/src/synth_unit.cc

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.ui.externaltools.ProgramBuilderLaunchConfigurationType">
<stringAttribute key="org.eclipse.debug.core.ATTR_REFRESH_SCOPE" value="${working_set:&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;resources&gt;&#10;&lt;item path=&quot;/MusicSynthesizer/libs&quot; type=&quot;2&quot;/&gt;&#10;&lt;/resources&gt;}"/>
<booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_BUILD_SCOPE" value="${working_set:&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;resources&gt;&#10;&lt;item path=&quot;/MusicSynthesizer/jni&quot; type=&quot;2&quot;/&gt;&#10;&lt;/resources&gt;}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${system_property:user.home}/install/android-ndk-r7b/ndk-build"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS" value="full,incremental,auto,"/>
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${project_loc}"/>
</launchConfiguration>

@ -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)

@ -0,0 +1 @@
APP_STL := stlport_static

@ -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

@ -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");
}
}

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

@ -0,0 +1,145 @@
#include <assert.h>
#include <stddef.h>
#include <jni.h>
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include <android/log.h>
#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);
}

@ -29,14 +29,15 @@
#define SynthMemoryBarrier() OSMemoryBarrier() #define SynthMemoryBarrier() OSMemoryBarrier()
#elif defined(__GNUC__) #elif defined(__GNUC__)
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) #if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)
#define SynthMemoryBarrier __sync_synchronize() #define SynthMemoryBarrier() __sync_synchronize()
#else
#warning Memory barrier is not enabled
#endif #endif
#warning Memory barrier is not enabled
#endif #endif
// #undef SynthMemoryBarrier() // #undef SynthMemoryBarrier()
// #define SynthMemoryBarrier()
#ifndef SynthMemoryBarrier
#warning Memory barrier is not enabled
#define SynthMemoryBarrier()
#endif
#endif // __SYNTH_H #endif // __SYNTH_H

@ -39,6 +39,7 @@ SynthUnit::SynthUnit(RingBuffer *ring_buffer) {
input_buffer_index_ = 0; input_buffer_index_ = 0;
std::memcpy(patch_data_, epiano, sizeof(epiano)); std::memcpy(patch_data_, epiano, sizeof(epiano));
current_patch_ = 0; current_patch_ = 0;
current_note_ = 0;
} }
// Transfer as many bytes as possible from ring buffer to input buffer. // Transfer as many bytes as possible from ring buffer to input buffer.

Loading…
Cancel
Save