我使用超能力,我需要将 midi 笔记发送到控制器 midi。
问题是我看到了一个功能send(int deviceID, unsigned char *data, int bytes);
在他们的源代码中说:
ID:设备标识符。
数据:原始 MIDI 数据。
字节:数 字节。
我不知道我需要准确地放在数据和字节上的值才能工作。
例如,原始 midi 可以是0x80 - 0x48 - 0x00
(C4 音符的开头,音高= 72,请参阅值(
和字节1001nnnn0kkkkkkk0kkkkkkk
(事件音符请参阅值(?
像这样:
SuperpoweredUSBMIDI::send(deviceID, reinterpret_cast(0x80 - 0x48 - 0x00(, 1001nnnn0kkkkkkk0kkkkkkk(;
总是崩溃的问题,由于我使用带有otg的手机来复制错误,我无法调试或出现错误。
当我找到解决方案时,我会尽快提出。
我是 markdown 的新手,对任何错误和我的英语语法表示抱歉。
编辑:我正在使用他们在GitHub上的示例项目进行测试,特别是simpleusb项目。(来源(
我做了一些小的修改和工作,但具体来说,我尝试了很多方法,什么都没有。我认为如果我很好地插入值
类simpleusb.cpp,这个简单的宏更改至少可以工作:
#include <jni.h>
#include <math.h>
#include <SuperpoweredCPU.h>
#include <AndroidIO/SuperpoweredUSBAudio.h>
#include <malloc.h>
#include <pthread.h>
// Called when the application is initialized. You can initialize SuperpoweredUSBSystem
// at any time btw. Although this function is marked __unused, it's due Android Studio's
// annoying warning only. It's definitely used.
__unused jint JNI_OnLoad (
JavaVM * __unused vm,
void * __unused reserved
) {
SuperpoweredUSBSystem::initialize(NULL, NULL, NULL, NULL, NULL);
return JNI_VERSION_1_6;
}
// Called when the application is closed. You can destroy SuperpoweredUSBSystem at any time btw.
// Although this function is marked __unused, it's due Android Studio's annoying warning only.
// It's definitely used.
__unused void JNI_OnUnload (
JavaVM * __unused vm,
void * __unused reserved
) {
SuperpoweredUSBSystem::destroy();
}
// A helper structure for sine wave output.
typedef struct sineWaveOutput {
float mul;
unsigned int step;
} sineWaveOutput;
// This is called periodically for audio I/O. Audio is always 32-bit floating point,
// regardless of the bit depth preference. (SuperpoweredUSBAudioProcessingCallback)
static bool audioProcessing (
void *clientdata,
int __unused deviceID,
float *audioIO,
int numberOfSamples,
int samplerate,
int __unused numInputChannels,
int numOutputChannels
) {
// If audioIO is NULL, then it's the very last call, IO is closing.
if (!audioIO) {
// Free memory for sine wave struct.
free(clientdata);
return true;
}
sineWaveOutput *swo = (sineWaveOutput *)clientdata;
if (swo->mul == 0.0f) swo->mul = (2.0f * float(M_PI) * 300.0f) / float(samplerate);
// Output sine wave on all output channels.
for (int n = 0; n < numberOfSamples; n++) {
float v = sinf(swo->step++ * swo->mul) * 0.5f;
for (int c = 0; c < numOutputChannels; c++) *audioIO++ = v;
}
return true; // Return false for silence, true if we put audio output into audioIO.
}
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static int latestMidiCommand = -1;
static int latestMidiChannel = 0;
static int latestMidiNumber = 0;
static int latestMidiValue = 0;
// This is called when some MIDI data is coming in.
// We are doing some primitive MIDI data processing here.
static void onMidiReceived (
void * __unused clientdata,
int __unused deviceID,
unsigned char *data,
int bytes
) {
while (bytes > 0) {
if (*data > 127) {
int command = *data >> 4;
switch (command) {
case 8: // note off
case 9: // note on
case 11: // control change
pthread_mutex_lock(&mutex);
// store incoming MIDI data
latestMidiCommand = command;
latestMidiChannel = *data++ & 15;
latestMidiNumber = *data++;
latestMidiValue = *data++;
pthread_mutex_unlock(&mutex);
bytes -= 3;
break;
default:
data++;
bytes--;
}
} else {
data++;
bytes--;
}
}
}
// Beautifying the ugly Java-C++ bridge (JNI) with these macros.
#define PID com_superpowered_simpleusb_SuperpoweredUSBAudio // Java package name and class name. Don't forget to update when you copy this code.
#define MAKE_JNI_FUNCTION(r, n, p) extern "C" JNIEXPORT r JNICALL Java_ ## p ## _ ## n
#define JNI(r, n, p) MAKE_JNI_FUNCTION(r, n, p)
// This is called by the SuperpoweredUSBAudio Java object when a USB device is connected.
JNI(jint, onConnect, PID) (
JNIEnv *env,
jobject __unused obj,
jint deviceID,
jint fd,
jbyteArray rawDescriptor
) {
jbyte *rd = env->GetByteArrayElements(rawDescriptor, NULL);
int dataBytes = env->GetArrayLength(rawDescriptor);
int r = SuperpoweredUSBSystem::onConnect(deviceID, fd, (unsigned char *)rd, dataBytes);
env->ReleaseByteArrayElements(rawDescriptor, rd, JNI_ABORT);
// r is 0 if SuperpoweredUSBSystem can't do anything with the connected device.
// r & 2 is true if the device has MIDI. Start receiving events.
if (r & 2) {
SuperpoweredUSBMIDI::startIO(deviceID, NULL, onMidiReceived);
//TODO HERE IT'S THE PROBLEM: error: integer literal is too large to be represented in any integer type
SuperpoweredUSBMIDI::send(deviceID, reinterpret_cast<unsigned char *>(0x80 - 0x48 - 0x00), 100100010011100000000011);
//FINISH PROBLEM
}
// r & 1 is true if the device has audio. Start output.
if (r & 1) {
// allocate struct for sine wave oscillator
sineWaveOutput *swo = (sineWaveOutput *)malloc(sizeof(sineWaveOutput));
if (swo) {
swo->mul = 0.0f;
swo->step = 0;
SuperpoweredCPU::setSustainedPerformanceMode(true);
// Our preferred settings: 44100 Hz, 16 bits, 0 input channels, 256 output channels,
// low latency. Superpowered will set up the audio device as close as it can to these.
SuperpoweredUSBAudio::easyIO (
deviceID, // deviceID
44100, // sampling rate
16, // bits per sample
0, // numInputChannels
256, // numOutputChannels
SuperpoweredUSBLatency_Low, // latency
swo, // clientData
audioProcessing // SuperpoweredUSBAudioProcessingCallback
);
}
}
return r;
}
// This is called by the SuperpoweredUSBAudio Java object when a USB device is disconnected.
JNI(void, onDisconnect, PID) (
JNIEnv * __unused env,
jobject __unused obj,
jint deviceID
) {
SuperpoweredUSBSystem::onDisconnect(deviceID);
SuperpoweredCPU::setSustainedPerformanceMode(false);
}
#undef PID
#define PID com_superpowered_simpleusb_MainActivity
// This is called by the MainActivity Java object periodically.
JNI(jintArray, getLatestMidiMessage, PID) (
JNIEnv *env,
jobject __unused obj
) {
jintArray ints = env->NewIntArray(4);
jint *i = env->GetIntArrayElements(ints, 0);
pthread_mutex_lock(&mutex);
i[0] = latestMidiCommand;
i[1] = latestMidiChannel;
i[2] = latestMidiNumber;
i[3] = latestMidiValue;
pthread_mutex_unlock(&mutex);
env->ReleaseIntArrayElements(ints, i, 0);
return ints;
}
另一个重要的类,但我在这个问题上没有改变,MainActivity:
@RequiresApi(api = Build.VERSION_CODES.M)
public class MainActivity extends AppCompatActivity implements SuperpoweredUSBAudioHandler {
private Handler handler;
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.text);
SuperpoweredUSBAudio usbAudio = new SuperpoweredUSBAudio(getApplicationContext(), this);
usbAudio.check();
// Update UI every 40 ms.
Runnable runnable = new Runnable() {
@Override
public void run() {
int[] midi = getLatestMidiMessage();
switch (midi[0]) {
case 8: textView.setText(String.format(Locale.ENGLISH, "Note Off, CH %d, %d, %d",
midi[1] + 1, midi[2], midi[3]));
break;
case 9: textView.setText(String.format(Locale.ENGLISH, "Note On, CH %d, %d, %d",
midi[1] + 1, midi[2], midi[3]));
break;
case 11: textView.setText(String.format(Locale.ENGLISH, "Control Change, CH %d, %d, %d",
midi[1] + 1, midi[2], midi[3]));
break;
}
handler.postDelayed(this, 40);
}
};
handler = new Handler();
handler.postDelayed(runnable, 40);
/*Not look, only for testing purposes and for remember what use.
byte[] buffer = new byte[32];
int numBytes = 0;
int channel = 6; // MIDI channels 1-16 are encoded as 0-15.
buffer[numBytes++] = (byte)(0x90 + (channel - 1)); // note on
buffer[numBytes++] = (byte)60; // pitch is middle C
buffer[numBytes++] = (byte)127; // max velocity
int offset = 0;*/
}
public void onUSBAudioDeviceAttached(int deviceIdentifier) {
}
public void onUSBMIDIDeviceAttached(int deviceIdentifier) {
}
public void onUSBDeviceDetached(int deviceIdentifier) {
}
// Function implemented in the native library.
private native int[] getLatestMidiMessage();
static {
System.loadLibrary("SuperpoweredExample");
}
}
我最终无法构建应用程序的错误:
Build command failed.
Error while executing process D:UsersramocAppDataLocalAndroidsdkcmake3.6.4111459bincmake.exe with arguments {--build F:PROYECTOFINSuperpoweredUSBExamplesimpleusb.externalNativeBuildcmakedebugarm64-v8a --target SuperpoweredExample}
[1/2] Building CXX object CMakeFiles/SuperpoweredExample.dir/simpleusb.cpp.o
FAILED: D:UsersramocAppDataLocalAndroidsdkndk-bundletoolchainsllvmprebuiltwindows-x86_64binclang++.exe --target=aarch64-none-linux-android --gcc-toolchain=D:/Users/ramoc/AppData/Local/Android/sdk/ndk-bundle/toolchains/aarch64-linux-android-4.9/prebuilt/windows-x86_64 --sysroot=D:/Users/ramoc/AppData/Local/Android/sdk/ndk-bundle/sysroot -DSuperpoweredExample_EXPORTS -IF:/PROYECTOFIN/SuperpoweredUSBExample/simpleusb/src/main/jni/src/main/jni -IF:/PROYECTOFIN/SuperpoweredUSBExample/simpleusb/../../../Superpowered -isystem D:/Users/ramoc/AppData/Local/Android/sdk/ndk-bundle/sources/cxx-stl/gnu-libstdc++/4.9/include -isystem D:/Users/ramoc/AppData/Local/Android/sdk/ndk-bundle/sources/cxx-stl/gnu-libstdc++/4.9/libs/arm64-v8a/include -isystem D:/Users/ramoc/AppData/Local/Android/sdk/ndk-bundle/sources/cxx-stl/gnu-libstdc++/4.9/include/backward -isystem D:/Users/ramoc/AppData/Local/Android/sdk/ndk-bundle/sysroot/usr/include/aarch64-linux-android -D__ANDROID_API__=21 -g -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -Wa,--noexecstack -Wformat -Werror=format-security -fsigned-char -IF:PROYECTOFINSuperpoweredUSBExamplesimpleusb......Superpowered -O0 -fno-limit-debug-info -fPIC -MD -MT CMakeFiles/SuperpoweredExample.dir/simpleusb.cpp.o -MF CMakeFilesSuperpoweredExample.dirsimpleusb.cpp.o.d -o CMakeFiles/SuperpoweredExample.dir/simpleusb.cpp.o -c F:PROYECTOFINSuperpoweredUSBExamplesimpleusbsrcmainjnisimpleusb.cpp
F:PROYECTOFINSuperpoweredUSBExamplesimpleusbsrcmainjnisimpleusb.cpp:129:100: error: integer literal is too large to be represented in any integer type
SuperpoweredUSBMIDI::send(deviceID, reinterpret_cast<unsigned char *>(0x80 - 0x48 - 0x00), 100100010011100000000011);
^
F:PROYECTOFINSuperpoweredUSBExamplesimpleusbsrcmainjnisimpleusb.cpp:129:100: warning: implicit conversion from 'unsigned long long' to 'int' changes value from 7976667151972931595 to 887068683 [-Wconstant-conversion]
SuperpoweredUSBMIDI::send(deviceID, reinterpret_cast<unsigned char *>(0x80 - 0x48 - 0x00), 100100010011100000000011);
~~~~~~~~~~~~~~~~~~~ ^~~~~~~~~~~~~~~~~~~~~~~~
1 warning and 1 error generated.
ninja: build stopped: subcommand failed.
也许是为了文档,对 jni 非常新手,或者对我来说太复杂了,现在无法 100% 理解。
好的,所以send
是这么说的:
send(int deviceID, unsigned char *data, int bytes);
发送到deviceId
指向名为data
的缓冲区的指针,该缓冲区具有一定数量的bytes
。
所以当你说: SuperpoweredUSBMIDI::send(deviceID, reinterpret_cast(0x80 - 0x48 - 0x00(, 100100010011100000000011(;
你本质上说的是"减去这 3 个数字:0x80 - 0x48 - 0x00
",然后将该数字重新解释为指向内存中某处缓冲区的指针。 内存中的缓冲区包含我希望您读取的100100010011100000000011
字节的数据。
为了解决这个问题,我们将发送如下数据:
unsigned char* send_buffer[32] = {0}; // zero out buffer to use as scratch
send_buffer[0] = 0x90;
send_buffer[1] = 0x48;
send_buffer[2] = 0x00;
SuperpoweredUSBMIDI::send(deviceID, send_buffer, 3);
我认为 MIDI 在序列后附加了一个校验和值(字节( - 这是在您的代码中还是在库代码中完成的?
消息应为无符号字符数组,并传递数组的地址(名称( 这就是 id 在我编程 midi 时在 C 中所做的。