AMidi API অ্যান্ড্রয়েড NDK r20b এবং এর পরবর্তী সংস্করণগুলোতে উপলব্ধ। এটি অ্যাপ ডেভেলপারদের C/C++ কোডের মাধ্যমে MIDI ডেটা প্রেরণ এবং গ্রহণ করার ক্ষমতা দেয়।
অ্যান্ড্রয়েড MIDI অ্যাপগুলি সাধারণত অ্যান্ড্রয়েড MIDI পরিষেবার সাথে যোগাযোগের জন্য midi API ব্যবহার করে। MIDI অ্যাপগুলি মূলত এক বা একাধিক MidiDevice অবজেক্ট খুঁজে বের করতে, খুলতে ও বন্ধ করতে এবং ডিভাইসের MIDI ইনপুট ও আউটপুট পোর্টের মাধ্যমে প্রতিটি ডিভাইসে ডেটা আদান-প্রদান করতে MidiManager এর উপর নির্ভর করে।
যখন আপনি AMidi ব্যবহার করেন, তখন একটি JNI কলের মাধ্যমে একটি MidiDevice এর অ্যাড্রেস নেটিভ কোড লেয়ারে পাঠান। সেখান থেকে, AMidi একটি AMidiDevice এর রেফারেন্স তৈরি করে, যেটিতে একটি MidiDevice এর বেশিরভাগ কার্যকারিতাই থাকে। আপনার নেটিভ কোড AMidi ফাংশন ব্যবহার করে যা সরাসরি একটি AMidiDevice সাথে যোগাযোগ করে। AMidiDevice সরাসরি MIDI সার্ভিসের সাথে সংযুক্ত হয়:
AMidi কল ব্যবহার করে, আপনি আপনার অ্যাপের C/C++ অডিও/কন্ট্রোল লজিককে MIDI ট্রান্সমিশনের সাথে নিবিড়ভাবে সংযুক্ত করতে পারেন। এতে JNI কল বা আপনার অ্যাপের জাভা সাইডে কলব্যাকের প্রয়োজন কম হয়। উদাহরণস্বরূপ, C কোডে তৈরি একটি ডিজিটাল সিন্থেসাইজার জাভা সাইড থেকে ইভেন্ট পাঠানোর জন্য JNI কলের অপেক্ষা না করে সরাসরি একটি AMidiDevice থেকে কী ইভেন্ট গ্রহণ করতে পারে। অথবা, একটি অ্যালগরিদমিক কম্পোজিং প্রক্রিয়া কী ইভেন্ট প্রেরণের জন্য জাভা সাইডে কলব্যাক না করেই সরাসরি একটি MIDI পারফরম্যান্স AMidiDevice এ পাঠাতে পারে।
যদিও AMidi MIDI ডিভাইসগুলির সাথে সরাসরি সংযোগ উন্নত করে, অ্যাপগুলিকে MidiDevice অবজেক্টগুলি খুঁজে বের করতে এবং খুলতে এখনও MidiManager ব্যবহার করতে হয়। এরপরের কাজটি AMidi করে নিতে পারে।
কখনও কখনও আপনার UI লেয়ার থেকে নেটিভ কোডে তথ্য পাঠানোর প্রয়োজন হতে পারে। উদাহরণস্বরূপ, যখন স্ক্রিনের বাটনগুলোর প্রতিক্রিয়ায় MIDI ইভেন্ট পাঠানো হয়। এটি করার জন্য আপনার নেটিভ লজিকে কাস্টম JNI কল তৈরি করুন। যদি UI আপডেট করার জন্য ডেটা ফেরত পাঠানোর প্রয়োজন হয়, তবে আপনি যথারীতি নেটিভ লেয়ার থেকে কলব্যাক করতে পারেন।
এই ডকুমেন্টটিতে একটি AMidi নেটিভ কোড অ্যাপ কীভাবে সেট আপ করতে হয় তা দেখানো হয়েছে এবং MIDI কমান্ড পাঠানো ও গ্রহণ করার উদাহরণও দেওয়া হয়েছে। একটি সম্পূর্ণ কার্যকরী উদাহরণের জন্য NativeMidi স্যাম্পল অ্যাপটি দেখুন।
AMidi ব্যবহার করুন
যেসব অ্যাপ AMidi ব্যবহার করে, সেগুলোর সেটআপ এবং বন্ধ করার পদ্ধতি একই, তা সে MIDI প্রেরণ বা গ্রহণ করুক, অথবা উভয়ই করুক।
স্টার্ট AMidi
জাভা অংশে, অ্যাপটিকে অবশ্যই সংযুক্ত একটি MIDI হার্ডওয়্যার খুঁজে বের করতে হবে, একটি সংশ্লিষ্ট MidiDevice তৈরি করতে হবে এবং সেটিকে নেটিভ কোডে পাঠাতে হবে।
- জাভা
MidiManagerক্লাসের মাধ্যমে MIDI হার্ডওয়্যার সম্পর্কে জানুন। - MIDI হার্ডওয়্যারের সাথে সঙ্গতিপূর্ণ একটি জাভা
MidiDeviceঅবজেক্ট সংগ্রহ করুন। - JNI ব্যবহার করে জাভা
MidiDeviceনেটিভ কোডে পাস করুন।
হার্ডওয়্যার এবং পোর্টগুলি আবিষ্কার করুন
ইনপুট এবং আউটপুট পোর্ট অবজেক্টগুলো অ্যাপের অংশ নয়। এগুলো মিডি ডিভাইসের পোর্টগুলোকে নির্দেশ করে। কোনো ডিভাইসে মিডি ডেটা পাঠাতে, একটি অ্যাপ একটি MIDIInputPort খোলে এবং তারপর তাতে ডেটা লেখে। বিপরীতভাবে, ডেটা গ্রহণ করতে, একটি অ্যাপ একটি MIDIOutputPort খোলে। সঠিকভাবে কাজ করার জন্য, অ্যাপটিকে অবশ্যই নিশ্চিত করতে হবে যে এটি যে পোর্টগুলো খুলছে তা সঠিক ধরনের। ডিভাইস এবং পোর্ট শনাক্তকরণের কাজটি জাভা সাইডে করা হয়।
এখানে এমন একটি পদ্ধতি রয়েছে যা প্রতিটি MIDI ডিভাইস শনাক্ত করে এবং এর পোর্টগুলো পরীক্ষা করে। এটি ডেটা গ্রহণের জন্য আউটপুট পোর্টসহ ডিভাইসগুলোর একটি তালিকা, অথবা ডেটা পাঠানোর জন্য ইনপুট পোর্টসহ ডিভাইসগুলোর একটি তালিকা ফেরত দেয়। একটি MIDI ডিভাইসে ইনপুট পোর্ট এবং আউটপুট পোর্ট উভয়ই থাকতে পারে।
কোটলিন
private fun getMidiDevices(isOutput: Boolean) : List{ if (isOutput) { return mMidiManager.devices.filter { it.outputPortCount > 0 } } else { return mMidiManager.devices.filter { it.inputPortCount > 0 } } }
জাভা
private ListgetMidiDevices(boolean isOutput){ ArrayList filteredMidiDevices = new ArrayList<>(); for (MidiDeviceInfo midiDevice : mMidiManager.getDevices()){ if (isOutput){ if (midiDevice.getOutputPortCount() > 0) filteredMidiDevices.add(midiDevice); } else { if (midiDevice.getInputPortCount() > 0) filteredMidiDevices.add(midiDevice); } } return filteredMidiDevices; }
আপনার C/C++ কোডে AMidi ফাংশন ব্যবহার করতে হলে আপনাকে অবশ্যই AMidi/AMidi.h অন্তর্ভুক্ত করতে হবে এবং amidi লাইব্রেরির সাথে লিঙ্ক করতে হবে। এই দুটিই Android NDK- তে পাওয়া যায়।
জাভা সাইড থেকে একটি JNI কলের মাধ্যমে নেটিভ লেয়ারে এক বা একাধিক MidiDevice অবজেক্ট এবং পোর্ট নম্বর পাঠাতে হবে। এরপর নেটিভ লেয়ার নিম্নলিখিত ধাপগুলো সম্পাদন করবে:
- প্রতিটি Java
MidiDeviceজন্যAMidiDevice_fromJava()ব্যবহার করে একটিAMidiDeviceসংগ্রহ করুন। -
AMidiInputPort_open()এবং/অথবাAMidiOutputPort_open()ব্যবহার করেAMidiDeviceথেকে একটিAMidiInputPortএবং/অথবাAMidiOutputPortসংগ্রহ করুন। - প্রাপ্ত পোর্টগুলো ব্যবহার করে MIDI ডেটা প্রেরণ এবং/অথবা গ্রহণ করুন।
স্টপ আমিডি
যখন জাভা অ্যাপটি আর MIDI ডিভাইসটি ব্যবহার করবে না, তখন রিসোর্স মুক্ত করার জন্য নেটিভ লেয়ারকে সংকেত দেওয়া উচিত। এর কারণ হতে পারে MIDI ডিভাইসটির সংযোগ বিচ্ছিন্ন হওয়া অথবা অ্যাপটি বন্ধ হয়ে যাওয়া।
MIDI রিসোর্স মুক্ত করতে, আপনার কোডকে নিম্নলিখিত কাজগুলো সম্পাদন করতে হবে:
- MIDI পোর্টে পড়া এবং/অথবা লেখা বন্ধ করুন। আপনি যদি ইনপুট পোল করার জন্য একটি রিডিং থ্রেড ব্যবহার করে থাকেন (নিচে 'একটি পোলিং লুপ বাস্তবায়ন করুন ' দেখুন), তাহলে থ্রেডটি বন্ধ করুন।
- যেকোনো খোলা
AMidiInputPortএবং/অথবাAMidiOutputPortঅবজেক্ট বন্ধ করতেAMidiInputPort_close()এবং/অথবাAMidiOutputPort_close()ফাংশন ব্যবহার করুন। -
AMidiDevice_release()ব্যবহার করেAMidiDeviceটি মুক্ত করুন।
MIDI ডেটা গ্রহণ করুন
MIDI গ্রহণকারী একটি MIDI অ্যাপের সাধারণ উদাহরণ হলো একটি "ভার্চুয়াল সিন্থেসাইজার", যা অডিও সিন্থেসিস নিয়ন্ত্রণের জন্য MIDI পারফরম্যান্স ডেটা গ্রহণ করে।
আগত MIDI ডেটা অ্যাসিঙ্ক্রোনাসভাবে গৃহীত হয়। তাই, একটি পৃথক থ্রেডে MIDI পড়াই শ্রেয়, যা ক্রমাগত এক বা একাধিক MIDI আউটপুট পোর্ট পোল করতে থাকে। এটি একটি ব্যাকগ্রাউন্ড থ্রেড বা একটি অডিও থ্রেড হতে পারে। পোর্ট থেকে পড়ার সময় AMidi ব্লক হয় না এবং তাই এটি একটি অডিও কলব্যাকের ভিতরে ব্যবহার করা নিরাপদ।
একটি MidiDevice এবং এর আউটপুট পোর্টগুলি সেট আপ করুন
একটি অ্যাপ কোনো ডিভাইসের আউটপুট পোর্ট থেকে আগত MIDI ডেটা পড়ে। আপনার অ্যাপের জাভা অংশকে অবশ্যই নির্ধারণ করতে হবে যে কোন ডিভাইস এবং পোর্টগুলো ব্যবহার করা হবে।
এই কোড স্নিপেটটি অ্যান্ড্রয়েডের MIDI সার্ভিস থেকে MidiManager তৈরি করে এবং এটি খুঁজে পাওয়া প্রথম ডিভাইসের জন্য একটি MidiDevice খোলে। MidiDevice খোলা হয়ে গেলে MidiManager.OnDeviceOpenedListener() -এর একটি ইনস্ট্যান্সে একটি কলব্যাক পাওয়া যায়। এই লিসেনারের onDeviceOpened মেথডটি কল করা হয়, যা এরপর ডিভাইসের আউটপুট পোর্ট ০ খোলার জন্য startReadingMidi() ফাংশনটিকে কল করে। এটি AppMidiManager.cpp ফাইলে সংজ্ঞায়িত একটি JNI ফাংশন। এই ফাংশনটি পরবর্তী কোড স্নিপেটে ব্যাখ্যা করা হয়েছে।
কোটলিন
//AppMidiManager.kt class AppMidiManager(context : Context) { private external fun startReadingMidi(midiDevice: MidiDevice, portNumber: Int) val mMidiManager : MidiManager = context.getSystemService(Context.MIDI_SERVICE) as MidiManager init { val midiDevices = getMidiDevices(true) // method defined in snippet above if (midiDevices.isNotEmpty()){ midiManager.openDevice(midiDevices[0], { startReadingMidi(it, 0) }, null) } } }
জাভা
//AppMidiManager.java public class AppMidiManager { private native void startReadingMidi(MidiDevice device, int portNumber); private MidiManager mMidiManager; AppMidiManager(Context context){ mMidiManager = (MidiManager) context.getSystemService(Context.MIDI_SERVICE); ListmidiDevices = getMidiDevices(true); // method defined in snippet above if (midiDevices.size() > 0){ mMidiManager.openDevice(midiDevices.get(0), new MidiManager.OnDeviceOpenedListener() { @Override public void onDeviceOpened(MidiDevice device) { startReadingMidi(device, 0); } },null); } } }
নেটিভ কোডটি জাভা-সাইডের MIDI ডিভাইস এবং এর পোর্টগুলোকে AMidi ফাংশন দ্বারা ব্যবহৃত রেফারেন্সে অনুবাদ করে।
এই হলো সেই JNI ফাংশন যা AMidiDevice_fromJava() কল করে একটি AMidiDevice তৈরি করে, এবং তারপর ডিভাইসটিতে একটি আউটপুট পোর্ট খোলার জন্য AMidiOutputPort_open() কল করে:
AppMidiManager.cpp
AMidiDevice midiDevice;
static pthread_t readThread;
static const AMidiDevice* midiDevice = AMIDI_INVALID_HANDLE;
static std::atomic<AMidiOutputPort*> midiOutputPort(AMIDI_INVALID_HANDLE);
void Java_com_nativemidiapp_AppMidiManager_startReadingMidi(
JNIEnv* env, jobject, jobject deviceObj, jint portNumber) {
AMidiDevice_fromJava(j_env, deviceObj, &midiDevice);
AMidiOutputPort* outputPort;
int32_t result =
AMidiOutputPort_open(midiDevice, portNumber, &outputPort);
// check for errors...
// Start read thread
int pthread_result =
pthread_create(&readThread, NULL, readThreadRoutine, NULL);
// check for errors...
}
একটি পোলিং লুপ বাস্তবায়ন করুন
যেসব অ্যাপ MIDI ডেটা গ্রহণ করে, তাদের অবশ্যই আউটপুট পোর্ট পোল করতে হবে এবং যখন AMidiOutputPort_receive() শূন্যের চেয়ে বড় কোনো সংখ্যা রিটার্ন করে, তখন সাড়া দিতে হবে।
MIDI স্কোপের মতো কম ব্যান্ডউইথের অ্যাপের জন্য, আপনি একটি নিম্ন-অগ্রাধিকারের ব্যাকগ্রাউন্ড থ্রেডে (উপযুক্ত স্লিপ ব্যবহার করে) পোল করতে পারেন।
যেসব অ্যাপ অডিও তৈরি করে এবং যাদের কঠোর রিয়েলটাইম পারফরম্যান্সের প্রয়োজনীয়তা রয়েছে, তারা মূল অডিও জেনারেশন কলব্যাকে (OpenSL ES-এর জন্য BufferQueue কলব্যাক, AAudio-তে AudioStream ডেটা কলব্যাক) পোল করতে পারে। যেহেতু AMidiOutputPort_receive() একটি নন-ব্লকিং ফাংশন, তাই এর পারফরম্যান্সের উপর খুব সামান্যই প্রভাব পড়ে।
উপরে startReadingMidi() ফাংশন থেকে কল করা readThreadRoutine() ফাংশনটি দেখতে এইরকম হতে পারে:
void* readThreadRoutine(void * /*context*/) {
uint8_t inDataBuffer[SIZE_DATABUFFER];
int32_t numMessages;
uint32_t opCode;
uint64_t timestamp;
reading = true;
while (reading) {
AMidiOutputPort* outputPort = midiOutputPort.load();
numMessages =
AMidiOutputPort_receive(outputPort, &opCode, inDataBuffer,
sizeof(inDataBuffer), ×tamp);
if (numMessages >= 0) {
if (opCode == AMIDI_OPCODE_DATA) {
// Dispatch the MIDI data….
}
} else {
// some error occurred, the negative numMessages is the error code
int32_t errorCode = numMessages;
}
}
}
নেটিভ অডিও এপিআই (যেমন OpenSL ES, বা AAudio) ব্যবহারকারী কোনো অ্যাপ অডিও জেনারেশন কলব্যাকে এইভাবে MIDI রিসিভ কোড যোগ করতে পারে:
void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void */*context*/)
{
uint8_t inDataBuffer[SIZE_DATABUFFER];
int32_t numMessages;
uint32_t opCode;
uint64_t timestamp;
// Read MIDI Data
numMessages = AMidiOutputPort_receive(outputPort, &opCode, inDataBuffer,
sizeof(inDataBuffer), ×tamp);
if (numMessages >= 0 && opCode == AMIDI_OPCODE_DATA) {
// Parse and respond to MIDI data
// ...
}
// Generate Audio…
// ...
}
নিম্নলিখিত ডায়াগ্রামটি একটি MIDI রিডিং অ্যাপের কার্যপ্রবাহ তুলে ধরেছে:

MIDI ডেটা পাঠান
MIDI রাইটিং অ্যাপের একটি সাধারণ উদাহরণ হলো MIDI কন্ট্রোলার বা সিকোয়েন্সার।
একটি MidiDevice এবং এর ইনপুট পোর্টগুলি সেট আপ করুন
একটি অ্যাপ একটি MIDI ডিভাইসের ইনপুট পোর্টগুলিতে বহির্গামী MIDI ডেটা লেখে। আপনার অ্যাপের জাভা অংশকে অবশ্যই নির্ধারণ করতে হবে যে কোন MIDI ডিভাইস এবং পোর্টগুলি ব্যবহার করা হবে।
নীচের এই সেটআপ কোডটি উপরের রিসিভিং উদাহরণটির একটি ভিন্ন রূপ। এটি অ্যান্ড্রয়েডের MIDI সার্ভিস থেকে MidiManager তৈরি করে। এরপর এটি খুঁজে পাওয়া প্রথম MidiDevice খোলে এবং ডিভাইসটির প্রথম ইনপুট পোর্টটি খোলার জন্য startWritingMidi() ফাংশনটি কল করে। এটি AppMidiManager.cpp ফাইলে সংজ্ঞায়িত একটি JNI কল। ফাংশনটি পরবর্তী কোড স্নিপেটে ব্যাখ্যা করা হয়েছে।
কোটলিন
//AppMidiManager.kt class AppMidiManager(context : Context) { private external fun startWritingMidi(midiDevice: MidiDevice, portNumber: Int) val mMidiManager : MidiManager = context.getSystemService(Context.MIDI_SERVICE) as MidiManager init { val midiDevices = getMidiDevices(false) // method defined in snippet above if (midiDevices.isNotEmpty()){ midiManager.openDevice(midiDevices[0], { startWritingMidi(it, 0) }, null) } } }
জাভা
//AppMidiManager.java public class AppMidiManager { private native void startWritingMidi(MidiDevice device, int portNumber); private MidiManager mMidiManager; AppMidiManager(Context context){ mMidiManager = (MidiManager) context.getSystemService(Context.MIDI_SERVICE); ListmidiDevices = getMidiDevices(false); // method defined in snippet above if (midiDevices.size() > 0){ mMidiManager.openDevice(midiDevices.get(0), new MidiManager.OnDeviceOpenedListener() { @Override public void onDeviceOpened(MidiDevice device) { startWritingMidi(device, 0); } },null); } } }
এই হলো সেই JNI ফাংশন যা AMidiDevice_fromJava() কল করে একটি AMidiDevice তৈরি করে, এবং তারপর ডিভাইসটিতে একটি ইনপুট পোর্ট খোলার জন্য AMidiInputPort_open() কল করে:
AppMidiManager.cpp
void Java_com_nativemidiapp_AppMidiManager_startWritingMidi(
JNIEnv* env, jobject, jobject midiDeviceObj, jint portNumber) {
media_status_t status;
status = AMidiDevice_fromJava(
env, midiDeviceObj, &sNativeSendDevice);
AMidiInputPort *inputPort;
status = AMidiInputPort_open(
sNativeSendDevice, portNumber, &inputPort);
// store it in a global
sMidiInputPort = inputPort;
}
MIDI ডেটা পাঠান
যেহেতু প্রেরিত MIDI ডেটার সময়কাল অ্যাপটি নিজেই ভালোভাবে বোঝে এবং নিয়ন্ত্রণ করে, তাই ডেটা প্রেরণ MIDI অ্যাপের প্রধান থ্রেডে করা যেতে পারে। তবে, পারফরম্যান্সের কারণে (সিকোয়েন্সারের মতো) MIDI তৈরি এবং প্রেরণ একটি পৃথক থ্রেডে করা যেতে পারে।
অ্যাপগুলো প্রয়োজনমতো MIDI ডেটা পাঠাতে পারে। মনে রাখবেন, ডেটা লেখার সময় AMidi ব্লক হয়ে যায়।
এখানে একটি JNI মেথডের উদাহরণ দেওয়া হলো, যা MIDI কমান্ডের একটি বাফার গ্রহণ করে এবং তা লিখে দেয়:
void Java_com_nativemidiapp_TBMidiManager_writeMidi(
JNIEnv* env, jobject, jbyteArray data, jint numBytes) {
jbyte* bufferPtr = env->GetByteArrayElements(data, NULL);
AMidiInputPort_send(sMidiInputPort, (uint8_t*)bufferPtr, numBytes);
env->ReleaseByteArrayElements(data, bufferPtr, JNI_ABORT);
}
নিম্নলিখিত ডায়াগ্রামটি একটি MIDI রাইটিং অ্যাপের কার্যপ্রবাহ তুলে ধরেছে:

কলব্যাক
যদিও এটি কঠোরভাবে AMidi-এর কোনো বৈশিষ্ট্য নয়, আপনার নেটিভ কোডের জাভা সাইডে ডেটা ফেরত পাঠানোর প্রয়োজন হতে পারে (উদাহরণস্বরূপ, UI আপডেট করার জন্য)। তা করার জন্য, আপনাকে জাভা সাইডে এবং নেটিভ লেয়ারে কোড লিখতে হবে:
- জাভা সাইডে একটি কলব্যাক মেথড তৈরি করুন।
- একটি JNI ফাংশন লিখুন যা কলব্যাকটি চালু করার জন্য প্রয়োজনীয় তথ্য সংরক্ষণ করে।
যখন কলব্যাক করার সময় আসে, আপনার নেটিভ কোড গঠন করতে পারে
এখানে জাভা-সাইডের কলব্যাক মেথড, onNativeMessageReceive() দেওয়া হলো:
কোটলিন
//MainActivity.kt private fun onNativeMessageReceive(message: ByteArray) { // Messages are received on some other thread, so switch to the UI thread // before attempting to access the UI runOnUiThread { showReceivedMessage(message) } }
জাভা
//MainActivity.java private void onNativeMessageReceive(final byte[] message) { // Messages are received on some other thread, so switch to the UI thread // before attempting to access the UI runOnUiThread(new Runnable() { public void run() { showReceivedMessage(message); } }); }
এখানে JNI ফাংশনের C কোড দেওয়া হল যা MainActivity.onNativeMessageReceive() -এর জন্য একটি কলব্যাক সেট আপ করে। জাভা MainActivity চালু হওয়ার সময় initNative() কল করে:
MainActivity.cpp
/**
* Initializes JNI interface stuff, specifically the info needed to call back into the Java
* layer when MIDI data is received.
*/
JNICALL void Java_com_example_nativemidi_MainActivity_initNative(JNIEnv * env, jobject instance) {
env->GetJavaVM(&theJvm);
// Setup the receive data callback (into Java)
jclass clsMainActivity = env->FindClass("com/example/nativemidi/MainActivity");
dataCallbackObj = env->NewGlobalRef(instance);
midDataCallback = env->GetMethodID(clsMainActivity, "onNativeMessageReceive", "([B)V");
}
যখন জাভাতে ডেটা ফেরত পাঠানোর সময় হয়, তখন নেটিভ কোডটি কলব্যাক পয়েন্টারগুলো পুনরুদ্ধার করে এবং কলব্যাকটি তৈরি করে:
AppMidiManager.cpp
// The Data Callback
extern JavaVM* theJvm; // Need this for allocating data buffer for...
extern jobject dataCallbackObj; // This is the (Java) object that implements...
extern jmethodID midDataCallback; // ...this callback routine
static void SendTheReceivedData(uint8_t* data, int numBytes) {
JNIEnv* env;
theJvm->AttachCurrentThread(&env, NULL);
if (env == NULL) {
LOGE("Error retrieving JNI Env");
}
// Allocate the Java array and fill with received data
jbyteArray ret = env->NewByteArray(numBytes);
env->SetByteArrayRegion (ret, 0, numBytes, (jbyte*)data);
// send it to the (Java) callback
env->CallVoidMethod(dataCallbackObj, midDataCallback, ret);
}
অতিরিক্ত সম্পদ
- AMidi রেফারেন্স
- সম্পূর্ণ নেটিভ মিডি স্যাম্পল অ্যাপটি গিটহাবে দেখুন।