C++程序  |  287行  |  11.82 KB

#include <atomic>
#include <inttypes.h>
#include <stdio.h>
#include <string.h>

#include <jni.h>

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

#include "messagequeue.h"

extern "C" {
JNIEXPORT jstring JNICALL Java_com_example_android_nativemididemo_NativeMidi_initAudio(
        JNIEnv* env, jobject thiz, jint sampleRate, jint playSamples);
JNIEXPORT void JNICALL Java_com_example_android_nativemididemo_NativeMidi_pauseAudio(
        JNIEnv* env, jobject thiz);
JNIEXPORT void JNICALL Java_com_example_android_nativemididemo_NativeMidi_resumeAudio(
        JNIEnv* env, jobject thiz);
JNIEXPORT void JNICALL Java_com_example_android_nativemididemo_NativeMidi_shutdownAudio(
        JNIEnv* env, jobject thiz);
JNIEXPORT jlong JNICALL Java_com_example_android_nativemididemo_NativeMidi_getPlaybackCounter(
        JNIEnv* env, jobject thiz);
JNIEXPORT jobjectArray JNICALL Java_com_example_android_nativemididemo_NativeMidi_getRecentMessages(
        JNIEnv* env, jobject thiz);
JNIEXPORT void JNICALL Java_com_example_android_nativemididemo_NativeMidi_startReadingMidi(
        JNIEnv* env, jobject thiz, jint deviceId, jint portNumber);
JNIEXPORT void JNICALL Java_com_example_android_nativemididemo_NativeMidi_stopReadingMidi(
        JNIEnv* env, jobject thiz);
}

static const char* errStrings[] = {
    "SL_RESULT_SUCCESS",                    // 0
    "SL_RESULT_PRECONDITIONS_VIOLATED",     // 1
    "SL_RESULT_PARAMETER_INVALID",          // 2
    "SL_RESULT_MEMORY_FAILURE",             // 3
    "SL_RESULT_RESOURCE_ERROR",             // 4
    "SL_RESULT_RESOURCE_LOST",              // 5
    "SL_RESULT_IO_ERROR",                   // 6
    "SL_RESULT_BUFFER_INSUFFICIENT",        // 7
    "SL_RESULT_CONTENT_CORRUPTED",          // 8
    "SL_RESULT_CONTENT_UNSUPPORTED",        // 9
    "SL_RESULT_CONTENT_NOT_FOUND",          // 10
    "SL_RESULT_PERMISSION_DENIED",          // 11
    "SL_RESULT_FEATURE_UNSUPPORTED",        // 12
    "SL_RESULT_INTERNAL_ERROR",             // 13
    "SL_RESULT_UNKNOWN_ERROR",              // 14
    "SL_RESULT_OPERATION_ABORTED",          // 15
    "SL_RESULT_CONTROL_LOST" };             // 16
static const char* getSLErrStr(int code) {
    return errStrings[code];
}

static SLObjectItf engineObject;
static SLEngineItf engineEngine;
static SLObjectItf outputMixObject;
static SLObjectItf playerObject;
static SLPlayItf playerPlay;
static SLAndroidSimpleBufferQueueItf playerBufferQueue;

static const int minPlaySamples = 32;
static const int maxPlaySamples = 1000;
static std::atomic_int playSamples(maxPlaySamples);
static short playBuffer[maxPlaySamples];

static std::atomic_ullong sharedCounter;

static AMIDI_Device* midiDevice = AMIDI_INVALID_HANDLE;
static std::atomic<AMIDI_OutputPort*> midiOutputPort(AMIDI_INVALID_HANDLE);

static int setPlaySamples(int newPlaySamples)
{
    if (newPlaySamples < minPlaySamples) newPlaySamples = minPlaySamples;
    if (newPlaySamples > maxPlaySamples) newPlaySamples = maxPlaySamples;
    playSamples.store(newPlaySamples);
    return newPlaySamples;
}

// Amount of messages we are ready to handle during one callback cycle.
static const size_t MAX_INCOMING_MIDI_MESSAGES = 20;
// Static allocation to save time in the callback.
static AMIDI_Message incomingMidiMessages[MAX_INCOMING_MIDI_MESSAGES];

static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void */*context*/)
{
    sharedCounter++;

    AMIDI_OutputPort* outputPort = midiOutputPort.load();
    if (outputPort != AMIDI_INVALID_HANDLE) {
        char midiDumpBuffer[1024];
        ssize_t midiReceived = AMIDI_receive(
                outputPort, incomingMidiMessages, MAX_INCOMING_MIDI_MESSAGES);
        if (midiReceived >= 0) {
            for (ssize_t i = 0; i < midiReceived; ++i) {
                AMIDI_Message* msg = &incomingMidiMessages[i];
                if (msg->opcode == AMIDI_OPCODE_DATA) {
                    memset(midiDumpBuffer, 0, sizeof(midiDumpBuffer));
                    int pos = snprintf(midiDumpBuffer, sizeof(midiDumpBuffer),
                            "%" PRIx64 " ", msg->timestamp);
                    for (uint8_t *b = msg->buffer, *e = b + msg->len; b < e; ++b) {
                        pos += snprintf(midiDumpBuffer + pos, sizeof(midiDumpBuffer) - pos,
                                "%02x ", *b);
                    }
                    nativemididemo::writeMessage(midiDumpBuffer);
                } else if (msg->opcode == AMIDI_OPCODE_FLUSH) {
                    nativemididemo::writeMessage("MIDI flush");
                }
            }
        } else {
            snprintf(midiDumpBuffer, sizeof(midiDumpBuffer),
                    "! MIDI Receive error: %s !", strerror(-midiReceived));
            nativemididemo::writeMessage(midiDumpBuffer);
        }
    }

    size_t usedBufferSize = playSamples.load() * sizeof(playBuffer[0]);
    if (usedBufferSize > sizeof(playBuffer)) {
        usedBufferSize = sizeof(playBuffer);
    }
    (*bq)->Enqueue(bq, playBuffer, usedBufferSize);
}

jstring Java_com_example_android_nativemididemo_NativeMidi_initAudio(
        JNIEnv* env, jobject, jint sampleRate, jint playSamples) {
    const char* stage;
    SLresult result;
    char printBuffer[1024];

    playSamples = setPlaySamples(playSamples);

    result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
    if (SL_RESULT_SUCCESS != result) { stage = "slCreateEngine"; goto handle_error; }

    result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
    if (SL_RESULT_SUCCESS != result) { stage = "realize Engine object"; goto handle_error; }

    result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
    if (SL_RESULT_SUCCESS != result) { stage = "get Engine interface"; goto handle_error; }

    result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, NULL, NULL);
    if (SL_RESULT_SUCCESS != result) { stage = "CreateOutputMix"; goto handle_error; }

    result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
    if (SL_RESULT_SUCCESS != result) { stage = "realize OutputMix object"; goto handle_error; }

    {
    SLDataFormat_PCM format_pcm = { SL_DATAFORMAT_PCM, 1, (SLuint32)sampleRate * 1000,
                                    SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
                                    SL_SPEAKER_FRONT_LEFT, SL_BYTEORDER_LITTLEENDIAN };
    SLDataLocator_AndroidSimpleBufferQueue loc_bufq =
            { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1 };
    SLDataSource audioSrc = { &loc_bufq, &format_pcm };
    SLDataLocator_OutputMix loc_outmix = { SL_DATALOCATOR_OUTPUTMIX, outputMixObject };
    SLDataSink audioSnk = { &loc_outmix, NULL };
    const SLInterfaceID ids[1] = { SL_IID_BUFFERQUEUE };
    const SLboolean req[1] = { SL_BOOLEAN_TRUE };
    result = (*engineEngine)->CreateAudioPlayer(
            engineEngine, &playerObject, &audioSrc, &audioSnk, 1, ids, req);
    if (SL_RESULT_SUCCESS != result) { stage = "CreateAudioPlayer"; goto handle_error; }

    result = (*playerObject)->Realize(playerObject, SL_BOOLEAN_FALSE);
    if (SL_RESULT_SUCCESS != result) { stage = "realize Player object"; goto handle_error; }
    }

    result = (*playerObject)->GetInterface(playerObject, SL_IID_PLAY, &playerPlay);
    if (SL_RESULT_SUCCESS != result) { stage = "get Play interface"; goto handle_error; }

    result = (*playerObject)->GetInterface(playerObject, SL_IID_BUFFERQUEUE, &playerBufferQueue);
    if (SL_RESULT_SUCCESS != result) { stage = "get BufferQueue interface"; goto handle_error; }

    result = (*playerBufferQueue)->RegisterCallback(playerBufferQueue, bqPlayerCallback, NULL);
    if (SL_RESULT_SUCCESS != result) { stage = "register BufferQueue callback"; goto handle_error; }

    result = (*playerBufferQueue)->Enqueue(playerBufferQueue, playBuffer, sizeof(playBuffer));
    if (SL_RESULT_SUCCESS != result) {
        stage = "enqueue into PlayerBufferQueue"; goto handle_error; }

    result = (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PLAYING);
    if (SL_RESULT_SUCCESS != result) {
        stage = "SetPlayState(SL_PLAYSTATE_PLAYING)"; goto handle_error; }

    snprintf(printBuffer, sizeof(printBuffer),
            "Success, sample rate %d, buffer samples %d", sampleRate, playSamples);
    return env->NewStringUTF(printBuffer);

handle_error:
    snprintf(printBuffer, sizeof(printBuffer), "Error at %s: %s", stage, getSLErrStr(result));
    return env->NewStringUTF(printBuffer);
}

void Java_com_example_android_nativemididemo_NativeMidi_pauseAudio(
        JNIEnv*, jobject) {
    if (playerPlay != NULL) {
        (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PAUSED);
    }
}

void Java_com_example_android_nativemididemo_NativeMidi_resumeAudio(
        JNIEnv*, jobject) {
    if (playerBufferQueue != NULL && playerPlay != NULL) {
        (*playerBufferQueue)->Enqueue(playerBufferQueue, playBuffer, sizeof(playBuffer));
        (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PLAYING);
    }
}

void Java_com_example_android_nativemididemo_NativeMidi_shutdownAudio(
        JNIEnv*, jobject) {
    if (playerObject != NULL) {
        (*playerObject)->Destroy(playerObject);
        playerObject = NULL;
        playerPlay = NULL;
        playerBufferQueue = NULL;
    }

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

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

jlong Java_com_example_android_nativemididemo_NativeMidi_getPlaybackCounter(JNIEnv*, jobject) {
    return sharedCounter.load();
}

jobjectArray Java_com_example_android_nativemididemo_NativeMidi_getRecentMessages(
        JNIEnv* env, jobject thiz) {
    return nativemididemo::getRecentMessagesForJava(env, thiz);
}

void Java_com_example_android_nativemididemo_NativeMidi_startReadingMidi(
        JNIEnv*, jobject, jlong deviceHandle, jint portNumber) {
    char buffer[1024];

    midiDevice = (AMIDI_Device*)deviceHandle;
//    int result = AMIDI_getDeviceById(deviceId, &midiDevice);
//    if (result == 0) {
//        snprintf(buffer, sizeof(buffer), "Obtained device token for uid %d: token %d", deviceId, midiDevice);
//    } else {
//        snprintf(buffer, sizeof(buffer), "Could not obtain device token for uid %d: %d", deviceId, result);
//    }
    nativemididemo::writeMessage(buffer);
//    if (result) return;

    AMIDI_DeviceInfo deviceInfo;
    int result = AMIDI_getDeviceInfo(midiDevice, &deviceInfo);
    if (result == 0) {
        snprintf(buffer, sizeof(buffer), "Device info: uid %d, type %d, priv %d, ports %d I / %d O",
                deviceInfo.uid, deviceInfo.type, deviceInfo.isPrivate,
                (int)deviceInfo.inputPortCount, (int)deviceInfo.outputPortCount);
    } else {
        snprintf(buffer, sizeof(buffer), "Could not obtain device info %d", result);
    }
    nativemididemo::writeMessage(buffer);
    if (result) return;

    AMIDI_OutputPort* outputPort;
    result = AMIDI_openOutputPort(midiDevice, portNumber, &outputPort);
    if (result == 0) {
        snprintf(buffer, sizeof(buffer), "Opened port %d: token %p", portNumber, outputPort);
        midiOutputPort.store(outputPort);
    } else {
        snprintf(buffer, sizeof(buffer), "Could not open port %p: %d", midiDevice, result);
    }
    nativemididemo::writeMessage(buffer);
}

void Java_com_example_android_nativemididemo_NativeMidi_stopReadingMidi(
        JNIEnv*, jobject) {
    AMIDI_OutputPort* outputPort = midiOutputPort.exchange(AMIDI_INVALID_HANDLE);
    if (outputPort == AMIDI_INVALID_HANDLE) return;
    int result = AMIDI_closeOutputPort(outputPort);
    char buffer[1024];
    if (result == 0) {
        snprintf(buffer, sizeof(buffer), "Closed port by token %p", outputPort);
    } else {
        snprintf(buffer, sizeof(buffer), "Could not close port by token %p: %d", outputPort, result);
    }
    nativemididemo::writeMessage(buffer);
}