ホームに戻る
 NDK OpenSL ES

// ステレオ、ループ再生、タッチで複数音再生
// android.mk に 「LOCAL_LDLIBS    += -lOpenSLES」 を追加
// 音ファイル 「bgm.h」 「se.h」 が必要です

#include <jni.h>
#include <errno.h>

#include <EGL/egl.h>
#include <GLES/gl.h>

#include <android/sensor.h>
#include <android/log.h>
#include <android_native_app_glue.h>

#include <stdlib.h>
#include <assert.h>
#include <string.h>

#include <SLES/OpenSLES.h>
#include "SLES/OpenSLES_Android.h"

#include "bgm.h"
#include "se.h"

#define PLAYER_NUM 2
#define OUTPUT_FRAMES 4096/2

static SLObjectItf engineObject = NULL;
static SLEngineItf engineEngine;

static SLObjectItf outputMixObject = NULL;

static SLObjectItf bqPlayerObject[PLAYER_NUM] = {NULL, NULL};
static SLPlayItf bqPlayerPlay[PLAYER_NUM];
static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue[PLAYER_NUM];

static unsigned bgm_index = 0;
static unsigned se_index = 0;

static short outputBuffer[PLAYER_NUM][OUTPUT_FRAMES];

static short *nextBuffer[PLAYER_NUM];
static unsigned nextSize[PLAYER_NUM];

// ループしてデータをセットし続ける
void setWave0()
{
  unsigned i;
  for(i = 0; i < OUTPUT_FRAMES; ++i){
    outputBuffer[0][i] = (unsigned char)bgm[bgm_index + 1] << 8;
    outputBuffer[0][i] += (unsigned char)bgm[bgm_index];
    bgm_index += 2;
    if(bgm_index == BGM_SIZE)bgm_index = 0;
  }
}

// 終点までデータをセットする 終点でなければ 1 終点にくれば 0 を返す
int setWave1()
{
  unsigned i;
  for(i = 0; i < OUTPUT_FRAMES; ++i){
    outputBuffer[1][i] = (unsigned char)se[se_index + 1] << 8;
    outputBuffer[1][i] += (unsigned char)se[se_index];
    se_index += 2;
    if(se_index == SE_SIZE){
      se_index = 0;
      return 0;
    }
  }

  return 1;
}

void bqPlayerCallback0(SLAndroidSimpleBufferQueueItf bq, void *context)
{
  SLresult result;

  if(bq != bqPlayerBufferQueue[0])return;
  if(context != NULL)return;

  // ループしてキューにデータを送り続ける
  setWave0();
  result = (*bqPlayerBufferQueue[0])->Enqueue(bqPlayerBufferQueue[0], nextBuffer[0], nextSize[0]);
  if(result != SL_RESULT_SUCCESS)return;
}

void bqPlayerCallback1(SLAndroidSimpleBufferQueueItf bq, void *context)
{
  SLresult result;

  if(bq != bqPlayerBufferQueue[1])return;
  if(context != NULL)return;

  if(setWave1() == 1){
    // 再生が終わっていなければキューへ押し込む 終わっていれば送らない
    result = (*bqPlayerBufferQueue[1])->Enqueue(bqPlayerBufferQueue[1], nextBuffer[1], nextSize[1]);
  }
  if(result == SL_RESULT_SUCCESS);
}

void createEngine()
{
  const SLInterfaceID ids[1] = {SL_IID_ENVIRONMENTALREVERB};
  const SLboolean req[1] = {SL_BOOLEAN_FALSE};

  SLresult result;

  result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
  if(result != SL_RESULT_SUCCESS)return;

  result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
  if(result != SL_RESULT_SUCCESS)return;

  result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
  if(result != SL_RESULT_SUCCESS)return;

  result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req);
  if(result != SL_RESULT_SUCCESS)return;

  result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
  if(result != SL_RESULT_SUCCESS)return;
}

void createBufferQueueAudioPlayer()
{
  SLresult result;
  unsigned i;

  SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1};
  SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, 2, SL_SAMPLINGRATE_44_1,
    SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
    SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT, SL_BYTEORDER_LITTLEENDIAN};
  SLDataSource audioSrc = {&loc_bufq, &format_pcm};

  SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
  SLDataSink audioSnk = {&loc_outmix, NULL};

  const SLInterfaceID ids[2] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_PLAY};
  const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};

  for(i = 0; i < PLAYER_NUM; i++){
    result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject[i], &audioSrc, &audioSnk, 2, ids, req);
    if(result != SL_RESULT_SUCCESS)return;
  }

  for(i = 0; i < PLAYER_NUM; i++){
    result = (*bqPlayerObject[i])->Realize(bqPlayerObject[i], SL_BOOLEAN_FALSE);
    if(result != SL_RESULT_SUCCESS)return;
  }

  for(i = 0; i < PLAYER_NUM; i++){
    result = (*bqPlayerObject[i])->GetInterface(bqPlayerObject[i], SL_IID_PLAY, &bqPlayerPlay[i]);
    if(result != SL_RESULT_SUCCESS)return;
  }

  for(i = 0; i < PLAYER_NUM; i++){
    result = (*bqPlayerObject[i])->GetInterface(bqPlayerObject[i], SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bqPlayerBufferQueue[i]);
    if(result != SL_RESULT_SUCCESS)return;
  }

  result = (*bqPlayerBufferQueue[0])->RegisterCallback(bqPlayerBufferQueue[0], bqPlayerCallback0, NULL);
  if(result != SL_RESULT_SUCCESS)return;

  result = (*bqPlayerBufferQueue[1])->RegisterCallback(bqPlayerBufferQueue[1], bqPlayerCallback1, NULL);
  if(result != SL_RESULT_SUCCESS)return;

  result = (*bqPlayerPlay[0])->SetPlayState(bqPlayerPlay[0], SL_PLAYSTATE_PLAYING);
  if(result != SL_RESULT_SUCCESS)return;

  result = (*bqPlayerPlay[1])->SetPlayState(bqPlayerPlay[1], SL_PLAYSTATE_PLAYING);
  if(result != SL_RESULT_SUCCESS)return;
}

void setStop(unsigned n)
{
  SLresult result;
  nextBuffer[n] = NULL;
  nextSize[n] = 0;
  result = (*bqPlayerBufferQueue[n])->Enqueue(bqPlayerBufferQueue[n], nextBuffer[n], nextSize[n]);
  result = (*bqPlayerPlay[n])->SetPlayState(bqPlayerPlay[n], SL_PLAYSTATE_PAUSED);
  if(result != SL_RESULT_SUCCESS)return;
}

void setTone(int n)
{
  SLresult result;
  if(n == 0){
    setWave0();
  }
  else if(n == 1){
    setWave1();
  }
  nextBuffer[n] = outputBuffer[n];
  nextSize[n] = sizeof(outputBuffer[n]);
  if(n == 0){
    // ループ再生を開始
    result = (*bqPlayerBufferQueue[n])->Enqueue(bqPlayerBufferQueue[n], nextBuffer[n], nextSize[n]);
  }
}

void shutdown()
{
  unsigned i;

  for(i = 0; i < PLAYER_NUM; i++){
    if(bqPlayerObject[i] != NULL){
      (*bqPlayerObject[i])->Destroy(bqPlayerObject[i]);
      bqPlayerObject[i] = NULL;
      bqPlayerPlay[i] = NULL;
      bqPlayerBufferQueue[i] = NULL;
    }
  }

  if(outputMixObject != NULL){
    (*outputMixObject)->Destroy(outputMixObject);
    outputMixObject = NULL;
  }

  if(engineObject != NULL){
    (*engineObject)->Destroy(engineObject);
    engineObject = NULL;
    engineEngine = NULL;
  }
}

struct engine{
  struct android_app* app;

  int32_t x;
  int32_t y;

  int animating;
  EGLDisplay display;
  EGLSurface surface;
  EGLContext context;
  int32_t width;
  int32_t height;
};

static int engine_init_display(struct engine* engine)
{
  const EGLint attribs[] = {
          EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
          EGL_BLUE_SIZE, 8,
          EGL_GREEN_SIZE, 8,
          EGL_RED_SIZE, 8,
          EGL_NONE
  };
  EGLint w, h, dummy, format;
  EGLint numConfigs;
  EGLConfig config;
  EGLSurface surface;
  EGLContext context;

  EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);

  eglInitialize(display, 0, 0);

  eglChooseConfig(display, attribs, &config, 1, &numConfigs);

  eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);

  ANativeWindow_setBuffersGeometry(engine->app->window, 0, 0, format);

  surface = eglCreateWindowSurface(display, config, engine->app->window, NULL);
  context = eglCreateContext(display, config, NULL, NULL);

  if(eglMakeCurrent(display, surface, surface, context) == EGL_FALSE){
    return -1;
  }

  eglQuerySurface(display, surface, EGL_WIDTH, &w);
  eglQuerySurface(display, surface, EGL_HEIGHT, &h);

  engine->display = display;
  engine->context = context;
  engine->surface = surface;
  engine->width = w;
  engine->height = h;

  glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
  glEnable(GL_CULL_FACE);
  glShadeModel(GL_SMOOTH);
  glDisable(GL_DEPTH_TEST);

    return 0;
}

static void engine_draw_frame(struct engine* engine) {
  if(engine->display == NULL){
    return;
  }

  glClearColor(((float)engine->x)/engine->width, 0.0, ((float)engine->y)/engine->height, 1);
  glClear(GL_COLOR_BUFFER_BIT);

  eglSwapBuffers(engine->display, engine->surface);
}

static void engine_term_display(struct engine* engine) {
  if(engine->display != EGL_NO_DISPLAY){
    eglMakeCurrent(engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    if(engine->context != EGL_NO_CONTEXT){
      eglDestroyContext(engine->display, engine->context);
    }
    if(engine->surface != EGL_NO_SURFACE){
      eglDestroySurface(engine->display, engine->surface);
    }
    eglTerminate(engine->display);
  }

  engine->animating = 0;
  engine->display = EGL_NO_DISPLAY;
  engine->context = EGL_NO_CONTEXT;
  engine->surface = EGL_NO_SURFACE;
}

static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) {
  struct engine* engine = (struct engine*)app->userData;
  if(AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION){
    engine->animating = 1;
    engine->x = AMotionEvent_getX(event, 0);
    engine->y = AMotionEvent_getY(event, 0);

    // キューに押し込むことで再生開始
    (*bqPlayerBufferQueue[1])->Enqueue(bqPlayerBufferQueue[1], nextBuffer[1], nextSize[1]);

    return 1;
  }

  return 0;
}

static void engine_handle_cmd(struct android_app* app, int32_t cmd) {
  struct engine* engine = (struct engine*)app->userData;
  switch(cmd) {
    case APP_CMD_INIT_WINDOW:
      if(engine->app->window != NULL){
        engine_init_display(engine);
        engine_draw_frame(engine);

        createEngine();
        createBufferQueueAudioPlayer();
        setTone(0);
        setTone(1);
      }
      break;
    case APP_CMD_TERM_WINDOW:
      engine_term_display(engine);
      setStop(0);
      setStop(1);
      shutdown();
      break;
    }
}

void android_main(struct android_app* state)
{
  struct engine engine;

  app_dummy();

  memset(&engine, 0, sizeof(engine));
  state->userData = &engine;
  state->onAppCmd = engine_handle_cmd;
  state->onInputEvent = engine_handle_input;
  engine.app = state;

  while (1) {
    int ident;
    int events;
    struct android_poll_source* source;

    while((ident=ALooper_pollAll(engine.animating ? 0 : -1, NULL, &events, (void**)&source)) >= 0){
      if(source != NULL){
        source->process(state, source);
      }

      if(state->destroyRequested != 0){
        engine_term_display(&engine);
        return;
      }

      if(engine.animating){
        engine_draw_frame(&engine);
      }
    }
  }
}

inserted by FC2 system