├── CMakeLists.txt ├── LICENSE ├── Makefile.am ├── README.md ├── RtMidi.cpp ├── RtMidi.h ├── android ├── README.md ├── app │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── rtmidi │ │ │ └── yellowlab │ │ │ └── midireader │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── cpp │ │ │ ├── CMakeLists.txt │ │ │ ├── RtMidi.cpp │ │ │ ├── RtMidi.h │ │ │ └── app-jni.cpp │ │ ├── java │ │ │ └── com │ │ │ │ ├── rtmidi │ │ │ │ └── yellowlab │ │ │ │ │ └── midireader │ │ │ │ │ ├── MainActivity.kt │ │ │ │ │ ├── MidiViewModel.kt │ │ │ │ │ └── ui.theme │ │ │ │ │ ├── Color.kt │ │ │ │ │ ├── Theme.kt │ │ │ │ │ └── Type.kt │ │ │ │ └── yellowlab │ │ │ │ └── rtmidi │ │ │ │ └── MidiDeviceOpenedListener.java │ │ └── res │ │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── drawable │ │ │ └── ic_launcher_background.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── themes.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── rtmidi │ │ └── yellowlab │ │ └── midireader │ │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── images │ └── midi.png └── settings.gradle ├── autogen.sh ├── cmake ├── RtMidiConfigUninstall.cmake.in └── rtmidi-config.cmake.in ├── configure.ac ├── contrib ├── go │ └── rtmidi │ │ ├── go.mod │ │ ├── rtmidi.go │ │ ├── rtmidi_stub.cpp │ │ ├── rtmidi_stub.h │ │ └── rtmidi_test.go └── java │ └── MidiDeviceOpenedListener.java ├── doc ├── Makefile.am ├── doxygen │ ├── Doxyfile.in │ ├── footer.html │ ├── header.html │ ├── samples │ │ └── getting_started.cpp │ └── tutorial.txt ├── images │ ├── ccrma.gif │ └── mcgill.gif └── release.txt ├── m4 └── ax_cxx_compile_stdcxx.m4 ├── msw ├── readme ├── rtmidilib.sln ├── rtmidilib.vcxproj ├── rtmidilib.vcxproj.filters └── rtmidilib.vcxproj.user ├── rtmidi-config.in ├── rtmidi.pc.in ├── rtmidi_c.cpp ├── rtmidi_c.h └── tests ├── Makefile.am ├── RtMidi.sln ├── apinames.cpp ├── apinames.vcxproj ├── apinames.vcxproj.filters ├── apinames.vcxproj.user ├── cmidiin.cpp ├── cmidiin.vcxproj ├── cmidiin.vcxproj.filters ├── cmidiin.vcxproj.user ├── midiclock.cpp ├── midiclock.vcxproj ├── midiclock.vcxproj.filters ├── midiclock.vcxproj.user ├── midiout.cpp ├── midiout.vcxproj ├── midiout.vcxproj.filters ├── midiout.vcxproj.user ├── midiprobe.cpp ├── midiprobe.vcxproj ├── midiprobe.vcxproj.filters ├── midiprobe.vcxproj.user ├── qmidiin.cpp ├── qmidiin.vcxproj ├── qmidiin.vcxproj.filters ├── qmidiin.vcxproj.user ├── sysextest.cpp ├── sysextest.vcxproj ├── sysextest.vcxproj.filters ├── sysextest.vcxproj.user ├── testcapi.c ├── testcapi.vcxproj ├── testcapi.vcxproj.filters └── testcapi.vcxproj.user /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | RtMidi: realtime MIDI i/o C++ classes 3 | Copyright (c) 2003-2023 Gary P. Scavone 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation files 7 | (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | Any person wishing to distribute modifications to the Software is 17 | asked to send the modifications to the original developer so that 18 | they can be incorporated into the canonical version. This is, 19 | however, not a binding provision of this license. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 25 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 26 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 27 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = . tests 2 | if MAKE_DOC 3 | SUBDIRS += doc 4 | endif 5 | 6 | lib_LTLIBRARIES = %D%/librtmidi.la 7 | %C%_librtmidi_la_CXXFLAGS = -DRTMIDI_EXPORT 8 | %C%_librtmidi_la_LDFLAGS = -no-undefined -export-dynamic -version-info @SO_VERSION@ 9 | %C%_librtmidi_la_SOURCES = \ 10 | %D%/RtMidi.cpp \ 11 | %D%/rtmidi_c.cpp 12 | 13 | rtmidi_incdir = $(includedir)/rtmidi 14 | rtmidi_inc_HEADERS = \ 15 | %D%/RtMidi.h \ 16 | %D%/rtmidi_c.h 17 | 18 | pkgconfigdatadir = $(libdir)/pkgconfig 19 | pkgconfigdata_DATA = rtmidi.pc 20 | 21 | EXTRA_DIST = autogen.sh README.md msw rtmidi-config.in contrib cmake CMakeLists.txt 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RtMidi 2 | 3 | ![Build Status](https://github.com/thestk/rtmidi/actions/workflows/ci.yml/badge.svg) 4 | [![Conan Center](https://shields.io/conan/v/rtmidi)](https://conan.io/center/rtmidi) 5 | 6 | A set of C++ classes that provide a common API for realtime MIDI input/output across Linux (ALSA & JACK), Macintosh OS X (CoreMIDI & JACK), Windows (Multimedia Library & UWP), Web MIDI, iOS and Android. 7 | 8 | By Gary P. Scavone, 2003-2023. 9 | 10 | This distribution of RtMidi contains the following: 11 | 12 | - `doc`: RtMidi documentation (also online at http://www.music.mcgill.ca/~gary/rtmidi/) 13 | - `tests`: example RtMidi programs 14 | 15 | On Unix systems, type `./configure` in the top level directory, then `make` in the `tests/` directory to compile the test programs. In Windows, open the Visual C++ workspace file located in the `tests/` directory. 16 | 17 | If you checked out the code from git, please run `./autogen.sh` before `./configure`. 18 | 19 | ## Overview 20 | 21 | RtMidi is a set of C++ classes (`RtMidiIn`, `RtMidiOut`, and API specific classes) that provide a common API (Application Programming Interface) for realtime MIDI input/output across Linux (ALSA, JACK), Macintosh OS X (CoreMIDI, JACK), and Windows (Multimedia Library) operating systems. RtMidi significantly simplifies the process of interacting with computer MIDI hardware and software. It was designed with the following goals: 22 | 23 | - object oriented C++ design 24 | - simple, common API across all supported platforms 25 | - only one header and one source file for easy inclusion in programming projects 26 | - MIDI device enumeration 27 | 28 | MIDI input and output functionality are separated into two classes, `RtMidiIn` and `RtMidiOut`. Each class instance supports only a single MIDI connection. RtMidi does not provide timing functionality (i.e., output messages are sent immediately). Input messages are timestamped with delta times in seconds (via a `double` floating point type). MIDI data is passed to the user as raw bytes using an `std::vector`. 29 | 30 | ## Windows 31 | 32 | In some cases, for example to use RtMidi with GS Synth, it may be necessary for your program to call `CoInitializeEx` and `CoUninitialize` on entry to and exit from the thread that uses RtMidi. 33 | 34 | ## Further reading 35 | 36 | For complete documentation on RtMidi, see the `doc` directory of the distribution or surf to http://www.music.mcgill.ca/~gary/rtmidi/. 37 | 38 | ## Legal and ethical 39 | 40 | The RtMidi license is similar to the MIT License, with the added *feature* that modifications be sent to the developer. Please see [LICENSE](LICENSE). 41 | -------------------------------------------------------------------------------- /android/README.md: -------------------------------------------------------------------------------- 1 | # Example Android Studio project 2 | 3 | Simple app using RtMidi. There are symlinks to 3 files in the main RtMidi project 4 | * RtMidi.cpp 5 | * RdMidi.h 6 | * MidiDeviceOpenedListener.java 7 | 8 | The main goal of this project is to demonstrate how to build RtMidi into an Android app. The 9 | app itself simply lists midi devices, opens the selected port and prints out any incoming packet 10 | 11 | ![Screenshot](./images/midi.png) 12 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | namespace 'com.rtmidi.yellowlab.midireader' 8 | compileSdk 33 9 | 10 | defaultConfig { 11 | applicationId "com.rtmidi.yellowlab.midireader" 12 | minSdk 31 13 | targetSdk 33 14 | versionCode 1 15 | versionName "1.0" 16 | 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | vectorDrawables { 19 | useSupportLibrary true 20 | } 21 | } 22 | 23 | buildTypes { 24 | release { 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 27 | } 28 | debug { 29 | jniDebuggable true 30 | } 31 | } 32 | compileOptions { 33 | sourceCompatibility JavaVersion.VERSION_1_8 34 | targetCompatibility JavaVersion.VERSION_1_8 35 | } 36 | kotlinOptions { 37 | jvmTarget = '1.8' 38 | } 39 | buildFeatures { 40 | compose true 41 | } 42 | composeOptions { 43 | kotlinCompilerExtensionVersion '1.4.8' 44 | } 45 | packagingOptions { 46 | resources { 47 | excludes += '/META-INF/{AL2.0,LGPL2.1}' 48 | } 49 | } 50 | externalNativeBuild { 51 | cmake { 52 | path file('src/main/cpp/CMakeLists.txt') 53 | version '3.22.1' 54 | } 55 | } 56 | buildFeatures { 57 | viewBinding true 58 | } 59 | } 60 | 61 | dependencies { 62 | 63 | implementation 'androidx.core:core-ktx:1.10.1' 64 | implementation platform('org.jetbrains.kotlin:kotlin-bom:1.9.0') 65 | implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1' 66 | implementation 'androidx.activity:activity-compose:1.7.2' 67 | implementation platform('androidx.compose:compose-bom:2023.06.01') 68 | implementation 'androidx.compose.ui:ui' 69 | implementation 'androidx.compose.ui:ui-graphics' 70 | implementation 'androidx.compose.ui:ui-tooling-preview' 71 | implementation 'androidx.compose.material3:material3' 72 | implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1' 73 | implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1' 74 | testImplementation 'junit:junit:4.13.2' 75 | androidTestImplementation 'androidx.test.ext:junit:1.1.5' 76 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' 77 | debugImplementation 'androidx.compose.ui:ui-tooling' 78 | debugImplementation 'androidx.compose.ui:ui-test-manifest' 79 | } -------------------------------------------------------------------------------- /android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /android/app/src/androidTest/java/com/rtmidi/yellowlab/midireader/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.rtmidi.yellowlab.midireader 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.rtmidi.yellowlab.midireader", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /android/app/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # For more information about using CMake with Android Studio, read the 2 | # documentation: https://d.android.com/studio/projects/add-native-code.html 3 | 4 | # Sets the minimum version of CMake required to build the native library. 5 | 6 | cmake_minimum_required(VERSION 3.22.1) 7 | 8 | # Declares and names the project. 9 | 10 | project("midireader") 11 | 12 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__AMIDI__") 13 | 14 | # Creates and names a library, sets it as either STATIC 15 | # or SHARED, and provides the relative paths to its source code. 16 | # You can define multiple libraries, and CMake builds them for you. 17 | # Gradle automatically packages shared libraries with your APK. 18 | 19 | add_library( # Sets the name of the library. 20 | midireader 21 | 22 | # Sets the library as a shared library. 23 | SHARED 24 | 25 | # Provides a relative path to your source file(s). 26 | RtMidi.cpp app-jni.cpp) 27 | 28 | # Searches for a specified prebuilt library and stores the path as a 29 | # variable. Because CMake includes system libraries in the search path by 30 | # default, you only need to specify the name of the public NDK library 31 | # you want to add. CMake verifies that the library exists before 32 | # completing its build. 33 | 34 | find_library( # Sets the name of the path variable. 35 | log-lib 36 | 37 | # Specifies the name of the NDK library that 38 | # you want CMake to locate. 39 | log) 40 | 41 | # Specifies libraries CMake should link to your target library. You 42 | # can link multiple libraries, such as libraries you define in this 43 | # build script, prebuilt third-party libraries, or system libraries. 44 | 45 | target_link_libraries( # Specifies the target library. 46 | midireader 47 | 48 | # Links the target library to the log library 49 | # included in the NDK. 50 | ${log-lib} amidi nativehelper) -------------------------------------------------------------------------------- /android/app/src/main/cpp/RtMidi.cpp: -------------------------------------------------------------------------------- 1 | ../../../../../RtMidi.cpp -------------------------------------------------------------------------------- /android/app/src/main/cpp/RtMidi.h: -------------------------------------------------------------------------------- 1 | ../../../../../RtMidi.h -------------------------------------------------------------------------------- /android/app/src/main/cpp/app-jni.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "RtMidi.h" 4 | 5 | static RtMidiIn* midiLib = new RtMidiIn( RtMidi::Api::ANDROID_AMIDI ); 6 | static jobject callbackObj = NULL; 7 | static jmethodID callbackMethod; 8 | static JavaVM* jvm; 9 | 10 | extern "C" JNIEXPORT jobjectArray JNICALL 11 | Java_com_rtmidi_yellowlab_midireader_MidiViewModel_portNames( 12 | JNIEnv* env, 13 | jobject /* this */) { 14 | 15 | auto portCount = midiLib->getPortCount(); 16 | auto names = env->NewObjectArray(portCount, env->FindClass("java/lang/String"), NULL); 17 | 18 | for (int i=0; igetPortName(i); 20 | env->SetObjectArrayElement(names, i, env->NewStringUTF(name.c_str())); 21 | } 22 | 23 | return names; 24 | } 25 | 26 | static void midicallback( double deltatime, std::vector< unsigned char > *message, void */*userData*/ ) { 27 | unsigned int numBytes = message->size(); 28 | 29 | JNIEnv* env; 30 | jvm->AttachCurrentThread(&env, NULL); 31 | 32 | // Allocate the Java array and fill with received data 33 | jbyteArray ret = env->NewByteArray(numBytes); 34 | env->SetByteArrayRegion(ret, 0, numBytes, (jbyte*)message); 35 | 36 | // send it to the (Java) callback 37 | env->CallVoidMethod(callbackObj, callbackMethod, deltatime, ret); 38 | } 39 | 40 | extern "C" 41 | JNIEXPORT void JNICALL 42 | Java_com_rtmidi_yellowlab_midireader_MidiViewModel_openPort( 43 | JNIEnv *env, 44 | jobject /* this */, 45 | jint port, 46 | jobject listener) { 47 | 48 | env->GetJavaVM(&jvm); 49 | 50 | midiLib->setCallback( midicallback ); 51 | midiLib->openPort(port); 52 | 53 | if (callbackObj) env->DeleteGlobalRef(callbackObj); 54 | callbackObj = env->NewGlobalRef(listener); 55 | callbackMethod = env->GetMethodID(env->GetObjectClass(listener), "onMidiMessage", "(D[B)V"); 56 | } 57 | 58 | extern "C" JNIEXPORT void JNICALL 59 | Java_com_rtmidi_yellowlab_midireader_MidiViewModel_closePort( 60 | JNIEnv *env, 61 | jobject /* this */) { 62 | 63 | midiLib->closePort(); 64 | if (callbackObj) env->DeleteGlobalRef(callbackObj); 65 | callbackObj = NULL; 66 | callbackMethod = NULL; 67 | } 68 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/rtmidi/yellowlab/midireader/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.rtmidi.yellowlab.midireader 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.activity.viewModels 7 | import androidx.compose.foundation.clickable 8 | import androidx.compose.foundation.layout.Box 9 | import androidx.compose.foundation.layout.Column 10 | import androidx.compose.foundation.layout.IntrinsicSize 11 | import androidx.compose.foundation.layout.Row 12 | import androidx.compose.foundation.layout.fillMaxHeight 13 | import androidx.compose.foundation.layout.fillMaxSize 14 | import androidx.compose.foundation.layout.fillMaxWidth 15 | import androidx.compose.foundation.layout.height 16 | import androidx.compose.foundation.layout.padding 17 | import androidx.compose.foundation.layout.width 18 | import androidx.compose.foundation.layout.wrapContentSize 19 | import androidx.compose.foundation.lazy.LazyColumn 20 | import androidx.compose.foundation.lazy.items 21 | import androidx.compose.foundation.shape.CutCornerShape 22 | import androidx.compose.material3.Button 23 | import androidx.compose.material3.ButtonDefaults.TextButtonContentPadding 24 | import androidx.compose.material3.Divider 25 | import androidx.compose.material3.DropdownMenu 26 | import androidx.compose.material3.DropdownMenuItem 27 | import androidx.compose.material3.MaterialTheme 28 | import androidx.compose.material3.Surface 29 | import androidx.compose.material3.Text 30 | import androidx.compose.runtime.Composable 31 | import androidx.compose.runtime.mutableStateOf 32 | import androidx.compose.runtime.remember 33 | import androidx.compose.ui.Alignment 34 | import androidx.compose.ui.Modifier 35 | import androidx.compose.runtime.getValue 36 | import androidx.compose.runtime.setValue 37 | import androidx.compose.ui.text.style.TextOverflow 38 | import androidx.compose.ui.unit.dp 39 | import com.rtmidi.yellowlab.midireader.ui.theme.MidiReaderTheme 40 | import androidx.lifecycle.viewmodel.compose.viewModel 41 | 42 | class MainActivity : ComponentActivity() { 43 | private val viewModel by viewModels() 44 | 45 | override fun onCreate(savedInstanceState: Bundle?) { 46 | super.onCreate(savedInstanceState) 47 | 48 | setContent { 49 | MidiReaderTheme { 50 | Surface( 51 | modifier = Modifier.fillMaxSize(), 52 | color = MaterialTheme.colorScheme.background 53 | ) { 54 | Column() { 55 | midiPortSelection(viewModel.ports) 56 | showEvents(events = viewModel.events) 57 | } 58 | } 59 | } 60 | } 61 | } 62 | } 63 | 64 | @Composable 65 | fun midiPortSelection(ports : List) { 66 | val viewModel = viewModel() 67 | 68 | Row(verticalAlignment = Alignment.CenterVertically, 69 | modifier = Modifier.height(IntrinsicSize.Max)) { 70 | Text(text = "Midi: ") 71 | midiPortsDropDown(ports, Modifier.weight(1f)) 72 | Button( 73 | shape = CutCornerShape(10), 74 | contentPadding = TextButtonContentPadding, 75 | onClick = { 76 | viewModel.enumerateMidiPorts() 77 | }) { 78 | Text(text = "Refresh") 79 | } 80 | } 81 | } 82 | 83 | @Composable 84 | fun midiPortsDropDown(ports: List, modifier: Modifier = Modifier) { 85 | val viewModel = viewModel() 86 | var expanded by remember { mutableStateOf(false) } 87 | var selectedIndex by remember { mutableStateOf(0) } 88 | 89 | Box(modifier = modifier.wrapContentSize(Alignment.TopStart)) { 90 | Text(ports[selectedIndex],modifier = Modifier 91 | .fillMaxWidth() 92 | .clickable(onClick = { expanded = true })) 93 | DropdownMenu( 94 | expanded = expanded, 95 | onDismissRequest = { expanded = false } 96 | ) { 97 | ports.forEachIndexed { index, s -> 98 | DropdownMenuItem(text = { Text(text = s) }, onClick = { 99 | selectedIndex = index 100 | expanded = false 101 | viewModel.selectPort(selectedIndex) 102 | }) 103 | } 104 | } 105 | } 106 | } 107 | 108 | @Composable 109 | fun showEvents(events: List) { 110 | LazyColumn(modifier = Modifier 111 | .fillMaxHeight() 112 | .padding(5.dp)) { 113 | items(events) { 114 | Row(modifier = Modifier.height(IntrinsicSize.Max)) { 115 | Text(text = it.time.toString(), modifier = Modifier.weight(0.6F), overflow = TextOverflow.Clip) 116 | Divider(modifier = Modifier.fillMaxHeight().width(1.dp)) 117 | Text(text = it.data, modifier = Modifier.weight(0.4F)) 118 | } 119 | } 120 | } 121 | } -------------------------------------------------------------------------------- /android/app/src/main/java/com/rtmidi/yellowlab/midireader/MidiViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.rtmidi.yellowlab.midireader 2 | 3 | import androidx.compose.runtime.mutableStateListOf 4 | import androidx.lifecycle.ViewModel 5 | 6 | data class MidiEvent( 7 | val time: Double = 0.0, 8 | val data: String = "" 9 | ) 10 | 11 | @ExperimentalUnsignedTypes 12 | fun ByteArray.toHex2(): String = asUByteArray().joinToString("") { it.toString(radix = 16).padStart(2, '0') } 13 | 14 | class MidiViewModel : ViewModel() { 15 | 16 | // JNI functions in app-jni.cpp 17 | external fun portNames(): Array 18 | external fun openPort(port: Int, listener: Any) 19 | external fun closePort(): Unit 20 | 21 | val ports = mutableStateListOf() 22 | val events = mutableStateListOf() 23 | 24 | init { 25 | ports.add("NONE") 26 | } 27 | 28 | fun enumerateMidiPorts() { 29 | ports.clear() 30 | ports.add("NONE") 31 | val midiPorts = portNames() 32 | ports.addAll(midiPorts) 33 | } 34 | 35 | fun selectPort(id: Int) { 36 | events.clear() 37 | 38 | if (id == 0) { 39 | closePort() 40 | return 41 | } 42 | 43 | openPort(id - 1, this) 44 | } 45 | 46 | companion object { 47 | init { 48 | System.loadLibrary("midireader") 49 | } 50 | } 51 | 52 | // The JNI code in app-jni.cpp calls this method back when it receives a 53 | // MIDI message from RrMidi. 54 | // 55 | // This callback is not part of the RtMidi library, it's how this demo app 56 | // uses RtMidi to update the UI. 57 | @Suppress("unused") 58 | fun onMidiMessage(time: Double, message: ByteArray) { 59 | var textMsg = message.toHex2() 60 | events.add(MidiEvent(time = time, data = textMsg)) 61 | } 62 | } -------------------------------------------------------------------------------- /android/app/src/main/java/com/rtmidi/yellowlab/midireader/ui.theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.rtmidi.yellowlab.midireader.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple80 = Color(0xFFD0BCFF) 6 | val PurpleGrey80 = Color(0xFFCCC2DC) 7 | val Pink80 = Color(0xFFEFB8C8) 8 | 9 | val Purple40 = Color(0xFF6650a4) 10 | val PurpleGrey40 = Color(0xFF625b71) 11 | val Pink40 = Color(0xFF7D5260) -------------------------------------------------------------------------------- /android/app/src/main/java/com/rtmidi/yellowlab/midireader/ui.theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package com.rtmidi.yellowlab.midireader.ui.theme 2 | 3 | import android.app.Activity 4 | import android.os.Build 5 | import androidx.compose.foundation.isSystemInDarkTheme 6 | import androidx.compose.material3.MaterialTheme 7 | import androidx.compose.material3.darkColorScheme 8 | import androidx.compose.material3.dynamicDarkColorScheme 9 | import androidx.compose.material3.dynamicLightColorScheme 10 | import androidx.compose.material3.lightColorScheme 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.runtime.SideEffect 13 | import androidx.compose.ui.graphics.toArgb 14 | import androidx.compose.ui.platform.LocalContext 15 | import androidx.compose.ui.platform.LocalView 16 | import androidx.core.view.WindowCompat 17 | 18 | private val DarkColorScheme = darkColorScheme( 19 | primary = Purple80, 20 | secondary = PurpleGrey80, 21 | tertiary = Pink80 22 | ) 23 | 24 | private val LightColorScheme = lightColorScheme( 25 | primary = Purple40, 26 | secondary = PurpleGrey40, 27 | tertiary = Pink40 28 | 29 | /* Other default colors to override 30 | background = Color(0xFFFFFBFE), 31 | surface = Color(0xFFFFFBFE), 32 | onPrimary = Color.White, 33 | onSecondary = Color.White, 34 | onTertiary = Color.White, 35 | onBackground = Color(0xFF1C1B1F), 36 | onSurface = Color(0xFF1C1B1F), 37 | */ 38 | ) 39 | 40 | @Composable 41 | fun MidiReaderTheme( 42 | darkTheme: Boolean = isSystemInDarkTheme(), 43 | // Dynamic color is available on Android 12+ 44 | dynamicColor: Boolean = true, 45 | content: @Composable () -> Unit 46 | ) { 47 | val colorScheme = when { 48 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { 49 | val context = LocalContext.current 50 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) 51 | } 52 | 53 | darkTheme -> DarkColorScheme 54 | else -> LightColorScheme 55 | } 56 | val view = LocalView.current 57 | if (!view.isInEditMode) { 58 | SideEffect { 59 | val window = (view.context as Activity).window 60 | window.statusBarColor = colorScheme.primary.toArgb() 61 | WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme 62 | } 63 | } 64 | 65 | MaterialTheme( 66 | colorScheme = colorScheme, 67 | typography = Typography, 68 | content = content 69 | ) 70 | } -------------------------------------------------------------------------------- /android/app/src/main/java/com/rtmidi/yellowlab/midireader/ui.theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.rtmidi.yellowlab.midireader.ui.theme 2 | 3 | import androidx.compose.material3.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.FontFamily 6 | import androidx.compose.ui.text.font.FontWeight 7 | import androidx.compose.ui.unit.sp 8 | 9 | // Set of Material typography styles to start with 10 | val Typography = Typography( 11 | bodyLarge = TextStyle( 12 | fontFamily = FontFamily.Default, 13 | fontWeight = FontWeight.Normal, 14 | fontSize = 16.sp, 15 | lineHeight = 24.sp, 16 | letterSpacing = 0.5.sp 17 | ) 18 | /* Other default text styles to override 19 | titleLarge = TextStyle( 20 | fontFamily = FontFamily.Default, 21 | fontWeight = FontWeight.Normal, 22 | fontSize = 22.sp, 23 | lineHeight = 28.sp, 24 | letterSpacing = 0.sp 25 | ), 26 | labelSmall = TextStyle( 27 | fontFamily = FontFamily.Default, 28 | fontWeight = FontWeight.Medium, 29 | fontSize = 11.sp, 30 | lineHeight = 16.sp, 31 | letterSpacing = 0.5.sp 32 | ) 33 | */ 34 | ) -------------------------------------------------------------------------------- /android/app/src/main/java/com/yellowlab/rtmidi/MidiDeviceOpenedListener.java: -------------------------------------------------------------------------------- 1 | ../../../../../../../../contrib/java/MidiDeviceOpenedListener.java -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thestk/rtmidi/ab1aca5153379e52e97b85b998b66b61619b7958/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thestk/rtmidi/ab1aca5153379e52e97b85b998b66b61619b7958/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thestk/rtmidi/ab1aca5153379e52e97b85b998b66b61619b7958/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thestk/rtmidi/ab1aca5153379e52e97b85b998b66b61619b7958/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thestk/rtmidi/ab1aca5153379e52e97b85b998b66b61619b7958/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thestk/rtmidi/ab1aca5153379e52e97b85b998b66b61619b7958/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thestk/rtmidi/ab1aca5153379e52e97b85b998b66b61619b7958/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thestk/rtmidi/ab1aca5153379e52e97b85b998b66b61619b7958/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thestk/rtmidi/ab1aca5153379e52e97b85b998b66b61619b7958/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thestk/rtmidi/ab1aca5153379e52e97b85b998b66b61619b7958/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Midi Reader 3 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |