├── .gitignore ├── .gitmodules ├── Info.plist ├── LICENSE.txt ├── Makefile ├── README.md ├── binaries ├── midi2osc.app.zip └── midi2osc.exe ├── imgui.ini ├── main.cpp ├── midi2osc.app └── Contents │ ├── Info.plist │ ├── MacOS │ ├── libglfw3.3.dylib │ ├── libportmidi.dylib │ └── midi2osc │ └── Resources │ ├── imgui.ini │ └── midi2osc.icns ├── midi2osc2.sln ├── midi2osc2.vcxproj ├── midi2osc2.vcxproj.filters ├── midi2oscIcon.png ├── portmidi_win ├── portmidi.h └── portmidi_s.lib └── screenshot.png /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/git,linux,macos,windows,visualstudio 3 | # Edit at https://www.gitignore.io/?templates=git,linux,macos,windows,visualstudio 4 | 5 | ### Git ### 6 | # Created by git for backups. To disable backups in Git: 7 | # $ git config --global mergetool.keepBackup false 8 | *.orig 9 | 10 | # Created by git when using merge tools for conflicts 11 | *.BACKUP.* 12 | *.BASE.* 13 | *.LOCAL.* 14 | *.REMOTE.* 15 | *_BACKUP_*.txt 16 | *_BASE_*.txt 17 | *_LOCAL_*.txt 18 | *_REMOTE_*.txt 19 | 20 | ### Linux ### 21 | *~ 22 | 23 | # temporary files which can be created if a process still has a handle open of a deleted file 24 | .fuse_hidden* 25 | 26 | # KDE directory preferences 27 | .directory 28 | 29 | # Linux trash folder which might appear on any partition or disk 30 | .Trash-* 31 | 32 | # .nfs files are created when an open file is removed but is still being accessed 33 | .nfs* 34 | 35 | ### macOS ### 36 | # General 37 | .DS_Store 38 | .AppleDouble 39 | .LSOverride 40 | 41 | # Icon must end with two \r 42 | Icon 43 | 44 | # Thumbnails 45 | ._* 46 | 47 | # Files that might appear in the root of a volume 48 | .DocumentRevisions-V100 49 | .fseventsd 50 | .Spotlight-V100 51 | .TemporaryItems 52 | .Trashes 53 | .VolumeIcon.icns 54 | .com.apple.timemachine.donotpresent 55 | 56 | # Directories potentially created on remote AFP share 57 | .AppleDB 58 | .AppleDesktop 59 | Network Trash Folder 60 | Temporary Items 61 | .apdisk 62 | 63 | ### Windows ### 64 | # Windows thumbnail cache files 65 | Thumbs.db 66 | ehthumbs.db 67 | ehthumbs_vista.db 68 | 69 | # Dump file 70 | *.stackdump 71 | 72 | # Folder config file 73 | [Dd]esktop.ini 74 | 75 | # Recycle Bin used on file shares 76 | $RECYCLE.BIN/ 77 | 78 | # Windows Installer files 79 | *.cab 80 | *.msi 81 | *.msix 82 | *.msm 83 | *.msp 84 | 85 | # Windows shortcuts 86 | *.lnk 87 | 88 | ### VisualStudio ### 89 | ## Ignore Visual Studio temporary files, build results, and 90 | ## files generated by popular Visual Studio add-ons. 91 | ## 92 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 93 | 94 | # User-specific files 95 | *.rsuser 96 | *.suo 97 | *.user 98 | *.userosscache 99 | *.sln.docstates 100 | 101 | # User-specific files (MonoDevelop/Xamarin Studio) 102 | *.userprefs 103 | 104 | # Mono auto generated files 105 | mono_crash.* 106 | 107 | # Build results 108 | [Dd]ebug/ 109 | [Dd]ebugPublic/ 110 | [Rr]elease/ 111 | [Rr]eleases/ 112 | x64/ 113 | x86/ 114 | [Aa][Rr][Mm]/ 115 | [Aa][Rr][Mm]64/ 116 | bld/ 117 | [Bb]in/ 118 | [Oo]bj/ 119 | [Ll]og/ 120 | 121 | # Visual Studio 2015/2017 cache/options directory 122 | .vs/ 123 | # Uncomment if you have tasks that create the project's static files in wwwroot 124 | #wwwroot/ 125 | 126 | # Visual Studio 2017 auto generated files 127 | Generated\ Files/ 128 | 129 | # MSTest test Results 130 | [Tt]est[Rr]esult*/ 131 | [Bb]uild[Ll]og.* 132 | 133 | # NUNIT 134 | *.VisualState.xml 135 | TestResult.xml 136 | 137 | # Build Results of an ATL Project 138 | [Dd]ebugPS/ 139 | [Rr]eleasePS/ 140 | dlldata.c 141 | 142 | # Benchmark Results 143 | BenchmarkDotNet.Artifacts/ 144 | 145 | # .NET Core 146 | project.lock.json 147 | project.fragment.lock.json 148 | artifacts/ 149 | 150 | # StyleCop 151 | StyleCopReport.xml 152 | 153 | # Files built by Visual Studio 154 | *_i.c 155 | *_p.c 156 | *_h.h 157 | *.ilk 158 | *.meta 159 | *.obj 160 | *.iobj 161 | *.pch 162 | *.pdb 163 | *.ipdb 164 | *.pgc 165 | *.pgd 166 | *.rsp 167 | *.sbr 168 | *.tlb 169 | *.tli 170 | *.tlh 171 | *.tmp 172 | *.tmp_proj 173 | *_wpftmp.csproj 174 | *.log 175 | *.vspscc 176 | *.vssscc 177 | .builds 178 | *.pidb 179 | *.svclog 180 | *.scc 181 | 182 | # Chutzpah Test files 183 | _Chutzpah* 184 | 185 | # Visual C++ cache files 186 | ipch/ 187 | *.aps 188 | *.ncb 189 | *.opendb 190 | *.opensdf 191 | *.sdf 192 | *.cachefile 193 | *.VC.db 194 | *.VC.VC.opendb 195 | 196 | # Visual Studio profiler 197 | *.psess 198 | *.vsp 199 | *.vspx 200 | *.sap 201 | 202 | # Visual Studio Trace Files 203 | *.e2e 204 | 205 | # TFS 2012 Local Workspace 206 | $tf/ 207 | 208 | # Guidance Automation Toolkit 209 | *.gpState 210 | 211 | # ReSharper is a .NET coding add-in 212 | _ReSharper*/ 213 | *.[Rr]e[Ss]harper 214 | *.DotSettings.user 215 | 216 | # JustCode is a .NET coding add-in 217 | .JustCode 218 | 219 | # TeamCity is a build add-in 220 | _TeamCity* 221 | 222 | # DotCover is a Code Coverage Tool 223 | *.dotCover 224 | 225 | # AxoCover is a Code Coverage Tool 226 | .axoCover/* 227 | !.axoCover/settings.json 228 | 229 | # Visual Studio code coverage results 230 | *.coverage 231 | *.coveragexml 232 | 233 | # NCrunch 234 | _NCrunch_* 235 | .*crunch*.local.xml 236 | nCrunchTemp_* 237 | 238 | # MightyMoose 239 | *.mm.* 240 | AutoTest.Net/ 241 | 242 | # Web workbench (sass) 243 | .sass-cache/ 244 | 245 | # Installshield output folder 246 | [Ee]xpress/ 247 | 248 | # DocProject is a documentation generator add-in 249 | DocProject/buildhelp/ 250 | DocProject/Help/*.HxT 251 | DocProject/Help/*.HxC 252 | DocProject/Help/*.hhc 253 | DocProject/Help/*.hhk 254 | DocProject/Help/*.hhp 255 | DocProject/Help/Html2 256 | DocProject/Help/html 257 | 258 | # Click-Once directory 259 | publish/ 260 | 261 | # Publish Web Output 262 | *.[Pp]ublish.xml 263 | *.azurePubxml 264 | # Note: Comment the next line if you want to checkin your web deploy settings, 265 | # but database connection strings (with potential passwords) will be unencrypted 266 | *.pubxml 267 | *.publishproj 268 | 269 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 270 | # checkin your Azure Web App publish settings, but sensitive information contained 271 | # in these scripts will be unencrypted 272 | PublishScripts/ 273 | 274 | # NuGet Packages 275 | *.nupkg 276 | # The packages folder can be ignored because of Package Restore 277 | **/[Pp]ackages/* 278 | # except build/, which is used as an MSBuild target. 279 | !**/[Pp]ackages/build/ 280 | # Uncomment if necessary however generally it will be regenerated when needed 281 | #!**/[Pp]ackages/repositories.config 282 | # NuGet v3's project.json files produces more ignorable files 283 | *.nuget.props 284 | *.nuget.targets 285 | 286 | # Microsoft Azure Build Output 287 | csx/ 288 | *.build.csdef 289 | 290 | # Microsoft Azure Emulator 291 | ecf/ 292 | rcf/ 293 | 294 | # Windows Store app package directories and files 295 | AppPackages/ 296 | BundleArtifacts/ 297 | Package.StoreAssociation.xml 298 | _pkginfo.txt 299 | *.appx 300 | *.appxbundle 301 | *.appxupload 302 | 303 | # Visual Studio cache files 304 | # files ending in .cache can be ignored 305 | *.[Cc]ache 306 | # but keep track of directories ending in .cache 307 | !?*.[Cc]ache/ 308 | 309 | # Others 310 | ClientBin/ 311 | ~$* 312 | *.dbmdl 313 | *.dbproj.schemaview 314 | *.jfm 315 | *.pfx 316 | *.publishsettings 317 | orleans.codegen.cs 318 | 319 | # Including strong name files can present a security risk 320 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 321 | #*.snk 322 | 323 | # Since there are multiple workflows, uncomment next line to ignore bower_components 324 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 325 | #bower_components/ 326 | 327 | # RIA/Silverlight projects 328 | Generated_Code/ 329 | 330 | # Backup & report files from converting an old project file 331 | # to a newer Visual Studio version. Backup files are not needed, 332 | # because we have git ;-) 333 | _UpgradeReport_Files/ 334 | Backup*/ 335 | UpgradeLog*.XML 336 | UpgradeLog*.htm 337 | ServiceFabricBackup/ 338 | *.rptproj.bak 339 | 340 | # SQL Server files 341 | *.mdf 342 | *.ldf 343 | *.ndf 344 | 345 | # Business Intelligence projects 346 | *.rdl.data 347 | *.bim.layout 348 | *.bim_*.settings 349 | *.rptproj.rsuser 350 | *- Backup*.rdl 351 | 352 | # Microsoft Fakes 353 | FakesAssemblies/ 354 | 355 | # GhostDoc plugin setting file 356 | *.GhostDoc.xml 357 | 358 | # Node.js Tools for Visual Studio 359 | .ntvs_analysis.dat 360 | node_modules/ 361 | 362 | # Visual Studio 6 build log 363 | *.plg 364 | 365 | # Visual Studio 6 workspace options file 366 | *.opt 367 | 368 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 369 | *.vbw 370 | 371 | # Visual Studio LightSwitch build output 372 | **/*.HTMLClient/GeneratedArtifacts 373 | **/*.DesktopClient/GeneratedArtifacts 374 | **/*.DesktopClient/ModelManifest.xml 375 | **/*.Server/GeneratedArtifacts 376 | **/*.Server/ModelManifest.xml 377 | _Pvt_Extensions 378 | 379 | # Paket dependency manager 380 | .paket/paket.exe 381 | paket-files/ 382 | 383 | # FAKE - F# Make 384 | .fake/ 385 | 386 | # CodeRush personal settings 387 | .cr/personal 388 | 389 | # Python Tools for Visual Studio (PTVS) 390 | __pycache__/ 391 | *.pyc 392 | 393 | # Cake - Uncomment if you are using it 394 | # tools/** 395 | # !tools/packages.config 396 | 397 | # Tabs Studio 398 | *.tss 399 | 400 | # Telerik's JustMock configuration file 401 | *.jmconfig 402 | 403 | # BizTalk build output 404 | *.btp.cs 405 | *.btm.cs 406 | *.odx.cs 407 | *.xsd.cs 408 | 409 | # OpenCover UI analysis results 410 | OpenCover/ 411 | 412 | # Azure Stream Analytics local run output 413 | ASALocalRun/ 414 | 415 | # MSBuild Binary and Structured Log 416 | *.binlog 417 | 418 | # NVidia Nsight GPU debugger configuration file 419 | *.nvuser 420 | 421 | # MFractors (Xamarin productivity tool) working folder 422 | .mfractor/ 423 | 424 | # Local History for Visual Studio 425 | .localhistory/ 426 | 427 | # BeatPulse healthcheck temp database 428 | healthchecksdb 429 | 430 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 431 | MigrationBackup/ 432 | 433 | # End of https://www.gitignore.io/api/git,linux,macos,windows,visualstudio 434 | 435 | ### Custom ### 436 | enc_temp_folder/ 437 | Debug/ 438 | Release/ 439 | midi2osc.ini 440 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "imgui"] 2 | path = imgui 3 | url = https://github.com/ocornut/imgui 4 | -------------------------------------------------------------------------------- /Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleGetInfoString 6 | MIDI2Osc 7 | CFBundleExecutable 8 | midi2osc 9 | CFBundleIdentifier 10 | com.mediamolecule.www 11 | CFBundleName 12 | midi2osc 13 | CFBundleIconFile 14 | midi2osc.icns 15 | CFBundleShortVersionString 16 | 0.01 17 | CFBundleInfoDictionaryVersion 18 | 6.0 19 | CFBundlePackageType 20 | APPL 21 | IFMajorVersion 22 | 0 23 | IFMinorVersion 24 | 1 25 | 26 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017-2018 Alex Evans 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | EXE = midi2osc 3 | SOURCES = main.cpp imgui/examples/imgui_impl_glfw.cpp imgui/examples/imgui_impl_opengl3.cpp 4 | SOURCES += imgui/imgui.cpp imgui/imgui_draw.cpp imgui/imgui_widgets.cpp 5 | SOURCES += imgui/examples/libs/gl3w/GL/gl3w.c 6 | OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) 7 | 8 | UNAME_S := $(shell uname -s) 9 | $(info Building for ${UNAME_S}) 10 | 11 | 12 | ifeq ($(UNAME_S), Linux) #LINUX 13 | ECHO_MESSAGE = "Linux" 14 | LIBS = -lGL -lpthread -lportmidi `pkg-config --static --libs glfw3` 15 | 16 | CXXFLAGS = -Iimgui/ -Iimgui/examples/libs/gl3w `pkg-config --cflags glfw3` 17 | CXXFLAGS += -Wall -Wformat -O2 18 | CFLAGS = $(CXXFLAGS) 19 | endif 20 | 21 | ifeq ($(UNAME_S), Darwin) #APPLE 22 | ECHO_MESSAGE = "Mac OS X" 23 | LIBS = -framework OpenGL -framework Cocoa -framework IOKit -framework CoreVideo 24 | LIBS += -L/usr/local/lib -lglfw3 -lportmidi -headerpad_max_install_names 25 | 26 | CXXFLAGS = -Iimgui/ -Iimgui/examples/libs/gl3w -Iimgui/examples/libs 27 | CXXFLAGS += -I/usr/local/include 28 | CXXFLAGS += -Wno-c++11-extensions 29 | CXXFLAGS += -Wall -Wformat -O2 30 | CFLAGS = $(CXXFLAGS) 31 | endif 32 | 33 | %.o:%.cpp 34 | $(CXX) $(CXXFLAGS) -c -o $@ $< 35 | 36 | %.o:imgui/%.cpp 37 | $(CXX) $(CXXFLAGS) -c -o $@ $< 38 | 39 | %.o:imgui/examples/%.cpp 40 | $(CXX) $(CXXFLAGS) -c -o $@ $< 41 | 42 | %.o:imgui/examples/libs/gl3w/GL/%.c 43 | $(CC) $(CFLAGS) -c -o $@ $< 44 | 45 | all: $(EXE) 46 | @echo Build complete for $(ECHO_MESSAGE) 47 | 48 | $(EXE): $(OBJS) $(EXE).icns Info.plist 49 | $(CXX) -o $@ $(OBJS) $(CXXFLAGS) $(LIBS) 50 | ifeq ($(UNAME_S), Darwin) #APPLE 51 | mkdir -p midi2osc.app/Contents/MacOS/ 52 | mkdir -p midi2osc.app/Contents/Resources/ 53 | cp midi2osc midi2osc.app/Contents/MacOS/ 54 | cp Info.plist midi2osc.app/Contents/ 55 | cp $(EXE).icns midi2osc.app/Contents/Resources/ 56 | -cp /usr/local/opt/glfw3/lib/libglfw3.3.dylib midi2osc.app/Contents/MacOS/ 57 | -cp /usr/local/opt/portmidi/lib/libportmidi.dylib midi2osc.app/Contents/MacOS/ 58 | install_name_tool -change /usr/local/opt/glfw3/lib/libglfw3.3.dylib @executable_path/libglfw3.3.dylib midi2osc.app/Contents/MacOS/midi2osc 59 | install_name_tool -change /usr/local/opt/portmidi/lib/libportmidi.dylib @executable_path/libportmidi.dylib midi2osc.app/Contents/MacOS/midi2osc 60 | endif 61 | 62 | clean: 63 | rm -f $(EXE) $(OBJS) 64 | rm -f $(EXE).icns 65 | 66 | $(EXE).icns: $(EXE)Icon.png 67 | ifeq ($(UNAME_S), Darwin) #APPLE 68 | rm -rf $(EXE).iconset 69 | mkdir $(EXE).iconset 70 | sips -z 16 16 $(EXE)Icon.png --out $(EXE).iconset/icon_16x16.png 71 | sips -z 32 32 $(EXE)Icon.png --out $(EXE).iconset/icon_16x16@2x.png 72 | sips -z 32 32 $(EXE)Icon.png --out $(EXE).iconset/icon_32x32.png 73 | sips -z 64 64 $(EXE)Icon.png --out $(EXE).iconset/icon_32x32@2x.png 74 | sips -z 128 128 $(EXE)Icon.png --out $(EXE).iconset/icon_128x128.png 75 | sips -z 256 256 $(EXE)Icon.png --out $(EXE).iconset/icon_128x128@2x.png 76 | sips -z 256 256 $(EXE)Icon.png --out $(EXE).iconset/icon_256x256.png 77 | sips -z 512 512 $(EXE)Icon.png --out $(EXE).iconset/icon_256x256@2x.png 78 | sips -z 512 512 $(EXE)Icon.png --out $(EXE).iconset/icon_512x512.png 79 | sips -z 1024 1024 $(EXE)Icon.png --out $(EXE).iconset/icon_512x512@2x.png 80 | cp $(EXE)Icon.png $(EXE).iconset/icon_512x512@2x.png 81 | iconutil -c icns -o $(EXE).icns $(EXE).iconset 82 | rm -r $(EXE).iconset 83 | endif 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | *Midi2Osc* 2 | 3 | this is a simple Midi to OpenSoundControl (aka OSC) bridge. 4 | it responds to midi messages on a midi input, and forwards them to OSC messages. 5 | see http://opensoundcontrol.org/ for more information about OSC. 6 | 7 | at the moment, it simply forwards all midi messages as a simple 'm' (midi) type OSC message to OSC path `/notes` 8 | 9 | Using 10 | ===== 11 | when the program runs, you should see a small window that looks like this: 12 | 13 | ![Screenshot](https://github.com/mmalex/midi2osc/blob/master/screenshot.png) 14 | 15 | choose a midi input device from the first drop down. if you don't have a hardware midi input interface, 16 | or you want to route midi from a DAW running on the same computer, you need a midi loopback device. 17 | on mac, you can use OSX's built in IAC driver. 18 | see https://help.ableton.com/hc/en-us/articles/209774225-Using-virtual-MIDI-buses 19 | for an example for how to set it up with ableton, but it applies to any software that can output midi. 20 | 21 | on windows, you can use a tool like midiox. 22 | 23 | clicking on the keyboard also generates midi events. if the midi input is working, you'll see messages at the bottom of the window. 24 | 25 | for the OSC output, type the IP address of the device you want to send the OSC messages to. 26 | it should be listening for OSC messages of type 'm' (midi) on OSC path /notes 27 | 28 | I'll leave you to experiment with the other controls 29 | 30 | Building 31 | ======== 32 | I've included pre-built binaries for mac and pc in the /binaries folder 33 | 34 | at the time of writing, the code has been compiled & tested on mac & windows. 35 | on mac, just run 'make'. you need to have portmidi and glfw3 installed. 36 | I used https://brew.sh/ to install these dependencies. 37 | 38 | on windows, only the 32-bit build is functional (linking to precompiled 32-bit portmidi static lib). 39 | I've included a portmidi static lib in the repo as I found it hard to compile from source. 40 | 41 | Dependencies 42 | ============ 43 | 44 | this little program uses the wonderful 'Dear Imgui' library by Omar Cornut 45 | https://github.com/ocornut/imgui 46 | 47 | it also uses the portmidi library for cross platform midi input 48 | http://portmedia.sourceforge.net/portmidi/ 49 | 50 | it also uses glfw and gl3w, embedded as part of the Dear Imgui submodule, since it is based on the glfw/gl3 imgui sample. 51 | 52 | Issues 53 | ====== 54 | This program is provided without any support by Alex Evans. 55 | It's licensed under the MIT license. 56 | Do what you want with it, but I do not have time to support it. 57 | in other words, use at your own risk! 58 | And to be clear, this is a personal project and is entirely unsupported by my employer. please don't hassle them. 59 | Although it is designed to be compatible with Dreams for the PS4, and that is the reason I wrote it, it is not part of Dreams, not endorsed by Sony or MediaMolecule. 60 | You also don't need Dreams to use it - it is a general 'midi to OSC' bridge, useful anywhere you want to send Midi messages to an OSC device or program. 61 | I just find it useful to use with dreams, YMMV. 62 | 63 | -------------------------------------------------------------------------------- /binaries/midi2osc.app.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmalex/midi2osc/4d883b84de0047365fd8d61fdaae5de5ac63cc57/binaries/midi2osc.app.zip -------------------------------------------------------------------------------- /binaries/midi2osc.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmalex/midi2osc/4d883b84de0047365fd8d61fdaae5de5ac63cc57/binaries/midi2osc.exe -------------------------------------------------------------------------------- /imgui.ini: -------------------------------------------------------------------------------- 1 | [Window][Debug##Default] 2 | Pos=60,60 3 | Size=400,400 4 | Collapsed=0 5 | 6 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #ifdef _WIN32 2 | #define _WINSOCK_DEPRECATED_NO_WARNINGS 3 | #define _CRT_SECURE_NO_WARNINGS 4 | #include // for sockets 5 | #include // for getaddrinfo 6 | #include 7 | #include 8 | #include 9 | #pragma comment(lib,"winmm.lib") // for midi 10 | #pragma comment(lib,"ws2_32.lib") // for sockets 11 | #pragma comment(lib,"opengl32.lib") // for wglGetProcAddress 12 | #pragma comment(lib,"portmidi_win/portmidi_s.lib") // for midi 13 | #ifdef _WIN64 14 | #pragma comment(lib,"imgui/examples/libs/glfw/lib-vc2010-64/glfw3.lib") // for imgui rendering 15 | #else 16 | #pragma comment(lib,"imgui/examples/libs/glfw/lib-vc2010-32/glfw3.lib") // for imgui rendering 17 | #endif 18 | #pragma comment(lib,"legacy_stdio_definitions.lib") // for vsnprintf 19 | #define close closesocket 20 | #define chdir _chdir 21 | #define strdup _strdup 22 | void usleep(int usec) { Sleep(usec/1000); } 23 | #else 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #ifdef __APPLE__ 30 | #include 31 | #endif 32 | #endif 33 | #define WINDOW_HEIGHT 500 34 | #include "imgui.h" 35 | #include "imgui/examples/imgui_impl_glfw.h" 36 | #include "imgui/examples/imgui_impl_opengl3.h" 37 | #include 38 | #include // This example is using gl3w to access OpenGL functions (because it is small). You may use glew/glad/glLoadGen/etc. whatever already works for you. 39 | #include 40 | #include 41 | #include 42 | #include 43 | typedef std::unique_lock ScopedLock; 44 | static void glfw_error_callback(int error, const char* description){ fprintf(stderr, "Error %d: %s\n", error, description);} 45 | int *inputids=0; 46 | char inifile[1024]; 47 | PortMidiStream *stream=0; 48 | int curport=-1; 49 | int quit=false; 50 | PmDeviceInfo *devices=0; 51 | std::mutex oscmutex; 52 | int keydown[128]={}; 53 | char kitname[256]="127.0.0.1"; 54 | const char* port="9000"; 55 | int topnote=127; 56 | int ccx=12,ccy=13,ccspicy=1; 57 | int xpos[16]={0},ypos[16]={0},spiciness[16]={0}; 58 | int highestnoteseen=0; 59 | int lastchan=0; 60 | int lastcontroller[16]={},lastlastcontroller[16]={}; 61 | int inmin=1,inmax=16; 62 | int outchan=1; 63 | int sustainmode=0; 64 | float velocitysens=100.f; 65 | float velocity2spicy=0.f; 66 | bool aftertouch2spicy=false; 67 | int lastnotes[16]={}; 68 | typedef uint32_t u32; 69 | int fd=-1; 70 | char ip[256]=""; 71 | struct addrinfo* addr=0; 72 | #undef min 73 | #undef max 74 | int min(int a,int b) { return (ab)?a:b; } 76 | int clamp(int x, int a, int b) { return min(max(x,a),b); } 77 | struct Message { 78 | // "/notes\0\0" - 8 79 | // ",m\0\0" - 4 80 | char header[12]="/notes\0\0,m\0"; 81 | unsigned char deviceidx=0; 82 | unsigned char msg=0; 83 | unsigned char data1=0; 84 | unsigned char data2=0; 85 | }; 86 | Message lastmsg; 87 | 88 | void TryToConnect() { 89 | if (fd==-1) { 90 | struct addrinfo hints={}; 91 | hints.ai_family=AF_UNSPEC; 92 | hints.ai_socktype=SOCK_DGRAM; 93 | hints.ai_protocol=0; 94 | hints.ai_flags=AI_ADDRCONFIG; 95 | printf("connecting to [%s] [%s]\n",ip,port); 96 | 97 | if (getaddrinfo(ip,port,&hints,&addr)!=0) printf("failed to resolve remote socket address"); 98 | else fd=socket(addr->ai_family, addr->ai_socktype,addr->ai_protocol); 99 | if (fd<0) printf("failed to open socket %s\n",strerror(errno)); 100 | if (fd<0) fd=-2; // -2 means failed 101 | } 102 | } 103 | 104 | void SendOSCMsg(const Message &msg) { 105 | TryToConnect(); 106 | struct addrinfo *a=addr; 107 | if (fd>=0 && a) { 108 | if (sendto(fd,(const char*)&msg,sizeof(msg),0,a->ai_addr,(int)a->ai_addrlen)<0) 109 | printf("failed to send udp packet!\n"); 110 | 111 | } 112 | } 113 | void ProcessMidiEvent(u32 dwParam1, bool mouse) { 114 | ScopedLock lock(oscmutex); 115 | Message msg; 116 | int midichan=(dwParam1 & 0xf); 117 | if (1) { 118 | // midi channel remap hack 119 | midichan++; 120 | if (midichaninmax) return ; // throw it away 121 | midichan+=(outchan-inmin); 122 | if (midichan<1) midichan=1; 123 | if (midichan>16) midichan=16; 124 | midichan--; 125 | dwParam1&=~15; 126 | dwParam1|=midichan; 127 | //////////////////////////////////// 128 | 129 | int chanbit=1<>8); 132 | msg.data2=(unsigned char)(dwParam1>>16); 133 | int miditype = (msg.msg & 0xf0) >> 4; 134 | int original_data1 = msg.data1; 135 | int original_data2 = msg.data2; 136 | unsigned char chan = msg.msg & 0xf; 137 | lastchan = chan; 138 | 139 | // remap zero-velocity note-on events to note-off ones 140 | if (miditype==9 && msg.data2 == 0) { 141 | msg.msg=(8<<4 | chan); 142 | miditype = 8; 143 | } 144 | 145 | // ignore advanced midi messages 146 | if (miditype < 0x8 || miditype > 0xe) return; 147 | 148 | if (miditype==8 || miditype==9) { 149 | // handle note-on and note-off events 150 | 151 | // update the highest detected note 152 | if (!mouse && msg.data1>highestnoteseen) highestnoteseen=msg.data1; 153 | 154 | // handle top note bound 155 | int shifted_note=msg.data1+127-topnote; 156 | if (msg.data1==topnote) msg.data1=127; 157 | if (msg.data1>=topnote-4 && msg.data1=0) { 234 | PmError e=Pm_OpenInput(&stream, curport, 0, 128, 0, 0); 235 | if (e) { 236 | printf("portmidi OpenInput error %d %s\n", e, Pm_GetErrorText(e)); 237 | openport=curport=-1; 238 | stream=0; 239 | } 240 | else 241 | printf("midi port '%s' opened ok\n",devices[openport].name); 242 | } 243 | } 244 | if (stream && Pm_Poll(stream)==TRUE) { 245 | PmEvent buffer[128]; 246 | int n=Pm_Read(stream, buffer, 128); 247 | if (n>0) { 248 | //printf("%d midi events!\n",n); 249 | for (int i=0;i=0) { 281 | printf("closing old socket\n"); 282 | int oldfd=fd; 283 | struct addrinfo *oldaddr=addr; 284 | fd=-1; 285 | addr=0; 286 | freeaddrinfo(oldaddr); 287 | close(oldfd); 288 | } 289 | // if (fd==-2) fd=-1; // try again automatically 290 | } 291 | int main(int argc, char**argv){ 292 | const char *home = 0; 293 | #ifdef __APPLE__ 294 | FSRef fsref; 295 | unsigned char path[PATH_MAX]; 296 | if (FSFindFolder(kUserDomain, kCurrentUserFolderType, kDontCreateFolder, &fsref) == noErr && FSRefMakePath(&fsref, path, sizeof(path)) == noErr) 297 | home=(const char*)path; 298 | #endif 299 | sprintf(inifile,"%s/midi2osc.ini",home?home:"."); 300 | printf("infile: [%s]\n",inifile); 301 | Pm_Initialize(); 302 | int nmidi = Pm_CountDevices(); 303 | int ninput=0; 304 | printf("%d midi devices\n",nmidi); 305 | devices=(PmDeviceInfo*)calloc(sizeof(PmDeviceInfo), nmidi); 306 | inputids=(int*)calloc(sizeof(int),nmidi); 307 | strcpy(kitname,GetIniSetting("IP","127.0.0.1")); 308 | const char *device=GetIniSetting("Device",""); 309 | inmin=strtol(GetIniSetting("MidiChannelInMin","1"),0,0); 310 | inmax=strtol(GetIniSetting("MidiChannelInMax","16"),0,0); 311 | outchan=strtol(GetIniSetting("MidiChannelOut","1"),0,0); 312 | ccspicy=strtol(GetIniSetting("Spicy","1"),0,0); 313 | ccx=strtol(GetIniSetting("X","12"),0,0); 314 | ccy=strtol(GetIniSetting("Y","13"),0,0); 315 | topnote=strtol(GetIniSetting("TopNote","127"),0,0); 316 | sustainmode=strtol(GetIniSetting("SustainMode","0"),0,0); 317 | velocitysens=float(strtol(GetIniSetting("VelocitySens","100"),0,0)); 318 | velocity2spicy=float(strtol(GetIniSetting("Velocity2Spicy","0"),0,0)); 319 | aftertouch2spicy=strtol(GetIniSetting("Aftertouch2SpicyEnable","0"),0,0)!=0; 320 | for (int i=0;iname,info->input, info->output); 324 | if (info->input) inputids[ninput++]=i; 325 | if (info->input && (curport<0 || (device[0] && strstr(info->name,device)))) 326 | curport=i; 327 | } 328 | if (ninput==0) { 329 | printf("no input devices"); 330 | } 331 | UpdateIP(); 332 | std::thread midithread(&MidiThread); 333 | // Setup window 334 | glfwSetErrorCallback(glfw_error_callback); 335 | if (!glfwInit()) 336 | return 1; 337 | // Decide GL+GLSL versions 338 | #if __APPLE__ 339 | // GL 3.2 + GLSL 150 340 | const char* glsl_version = "#version 150"; 341 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 342 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); 343 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only 344 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac 345 | #else 346 | // GL 3.0 + GLSL 130 347 | const char* glsl_version = "#version 130"; 348 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 349 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); 350 | //glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only 351 | //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only 352 | #endif 353 | 354 | GLFWwindow* window = glfwCreateWindow(640, WINDOW_HEIGHT, "midi2osc", NULL, NULL); 355 | glfwSetWindowSizeLimits(window, 640, WINDOW_HEIGHT, 640, WINDOW_HEIGHT); 356 | glfwMakeContextCurrent(window); 357 | glfwSwapInterval(1); // Enable vsync 358 | gl3wInit(); 359 | // Setup ImGui binding 360 | ImGui::CreateContext(); 361 | ImGuiIO& io = ImGui::GetIO(); (void)io; 362 | io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls 363 | //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls 364 | ImGui_ImplGlfw_InitForOpenGL(window, true); 365 | ImGui::StyleColorsDark(); 366 | 367 | ImGui_ImplOpenGL3_Init(glsl_version); 368 | 369 | //ImGui::StyleColorsClassic(); 370 | ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); 371 | while (!glfwWindowShouldClose(window)) 372 | { 373 | glfwPollEvents(); 374 | ImGui_ImplOpenGL3_NewFrame(); 375 | ImGui_ImplGlfw_NewFrame(); 376 | ImGui::NewFrame(); 377 | 378 | ImGui::SetNextWindowPos(ImVec2(0, 0), 0); // ImGuiCond_FirstUseEver 379 | ImGui::SetNextWindowSize(ImVec2(640,WINDOW_HEIGHT), 0); 380 | ImGui::Begin("midi2osc", 0, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings); 381 | int port=0; 382 | for (int i=0;ispiciness", &velocity2spicy, 0.f, 100.f, "%2.0f%%")) { 415 | SaveIniSettings(); 416 | } 417 | if (ImGui::Checkbox("midi aftertouch->spiciness enable", &aftertouch2spicy)) { 418 | SaveIniSettings(); 419 | } 420 | 421 | ImGui::Separator(); 422 | ImGui::PushItemWidth(100.f); 423 | if (ImGui::DragInt("highest note", &topnote, 1, 0, 127)) { 424 | SaveIniSettings(); 425 | } 426 | ImGui::SameLine(); 427 | if (ImGui::Button("learn")) { 428 | topnote=highestnoteseen; 429 | SaveIniSettings(); 430 | } 431 | if (ImGui::DragInt("spicy CC",&ccspicy,1,0,127)) { 432 | SaveIniSettings(); 433 | } 434 | ImGui::SameLine(); 435 | if (ImGui::Button("learn##cc")) { 436 | ccspicy=lastcontroller[lastchan]; 437 | SaveIniSettings(); 438 | } 439 | if (ImGui::DragInt("##X CC",&ccx,1,0,127)) { 440 | SaveIniSettings(); 441 | } 442 | ImGui::SameLine(); 443 | if (ImGui::DragInt("XY CCs",&ccy,1,0,127)) { 444 | SaveIniSettings(); 445 | } 446 | ImGui::SameLine(); 447 | ImGui::PopItemWidth(); 448 | if (ImGui::Button("learn##xy")) { 449 | ccx=min(lastcontroller[lastchan],lastlastcontroller[lastchan]); 450 | ccy=max(lastcontroller[lastchan],lastlastcontroller[lastchan]); 451 | SaveIniSettings(); 452 | } 453 | // draw a keyboard 454 | ImDrawList* draw_list = ImGui::GetWindowDrawList(); 455 | float pw=ImGui::GetWindowContentRegionWidth(); 456 | float ph=64.f; 457 | float kw=pw/75.f; 458 | const ImVec2 p = ImGui::GetCursorScreenPos(); 459 | const ImVec2 q(p.x+pw,p.y+ph); 460 | draw_list->AddRectFilled(ImVec2(q.x-64.f,p.y-66.f),ImVec2(q.x,p.y-2.f),0xff808080,2.f); 461 | draw_list->AddRectFilled(ImVec2(q.x-70.f,p.y-66.f),ImVec2(q.x-66.f,p.y-2.f),0xff808080,2.f); 462 | draw_list->AddRectFilled(ImVec2(q.x-70.f,p.y-2.f-spiciness[0]*0.5f),ImVec2(q.x-66.f,p.y-2.f),0xff0000ff,2.f); 463 | float xx=q.x-64.f+xpos[0]*0.5f,yy=p.y-2.f-ypos[0]*0.5f; 464 | draw_list->AddLine(ImVec2(xx,p.y-66.f),ImVec2(xx,p.y-2.f),0x80000000,1.f); 465 | draw_list->AddLine(ImVec2(q.x-64.f,yy),ImVec2(q.x,yy),0x80000000,1.f); 466 | draw_list->AddRectFilled(p,q,0xff000000); 467 | draw_list->AddCircleFilled(ImVec2(xx+0.5f,yy+0.5f),2.f,0xff0000ff,8); 468 | const ImVec2 m=ImGui::GetIO().MousePos; 469 | int mb=ImGui::GetIO().MouseDown[0]; 470 | static int mousekey=-1; 471 | if (!mb && mousekey>=0) { 472 | ProcessMidiEvent(0x80+(mousekey<<8),true); 473 | mousekey=-1; 474 | } 475 | int keytab[2][7]={ 476 | {0,2,4,5,7,9,11}, 477 | {1,3,-10000,6,8,10,-10000}}; 478 | for (int blackwhite=0;blackwhite<2;++blackwhite) 479 | for (int i=0;i<75;++i) { 480 | int midinote=keytab[blackwhite][i%7]+12*(i/7); 481 | if (midinote<0 || midinote>127) continue; 482 | 483 | ImU32 col=0xffeeeeee; 484 | if (midinote>topnote) col=0x80eeeeee; 485 | if (blackwhite) col^=0xdddddd; 486 | if (midinote==topnote) col=0xffffee00; 487 | ImVec2 a,b; 488 | float blacky=p.y+ph*0.6f; 489 | if (blackwhite) 490 | a=ImVec2(p.x+kw*i+kw*0.66f,p.y),b=ImVec2(p.x+kw*i+kw*1.33f,blacky); 491 | else 492 | a=ImVec2(p.x+kw*i,p.y),b=ImVec2(p.x+kw*i+kw-1.f,q.y); 493 | if (m.x>=a.x && m.x<=b.x && m.y>=a.y && m.y<=b.y && (m.y=0 && mousekey!=midinote) { 496 | ProcessMidiEvent(0x80+(mousekey<<8),true); 497 | mousekey=-1; 498 | } 499 | if (mousekey!=midinote) { 500 | mousekey=midinote; 501 | ProcessMidiEvent(0x90+(mousekey<<8)+(127<<16),true); 502 | } 503 | } 504 | col^=0x4040; 505 | } 506 | if (keydown[midinote]) col^=0x8080; 507 | draw_list->AddRectFilled(a,b, col, 2.f); 508 | } 509 | ImGui::Dummy(ImVec2(pw,ph)); 510 | if (ImGui::Button("PANIC all notes up")) { 511 | for (int chan=0;chan<16;++chan) 512 | ProcessMidiEvent(0x7bb0+chan,true); 513 | 514 | } 515 | ImGui::Separator(); 516 | ImGui::Text("channel %d, spicy=%d, xy=%d,%d; highest note seen=%d, last ccs seen=%d,%d", lastchan+1, spiciness[lastchan],xpos[lastchan],ypos[lastchan],highestnoteseen, lastcontroller[lastchan], lastlastcontroller[lastchan]); 517 | const char *types[16]={ 518 | 0,0,0,0,0,0,0,0, 519 | "note up","note down","aftertouch","cc","program","channel pressure","pitchbend","sysex" 520 | }; 521 | ImGui::Text("last midi message: chan %01x %02x %02x %s",lastmsg.msg&15,lastmsg.data1,lastmsg.data2,types[lastmsg.msg>>4]); 522 | ImGui::End(); 523 | // Rendering 524 | int display_w, display_h; 525 | glfwGetFramebufferSize(window, &display_w, &display_h); 526 | glViewport(0, 0, display_w, display_h); 527 | glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); 528 | glClear(GL_COLOR_BUFFER_BIT); 529 | ImGui::Render(); 530 | 531 | ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); 532 | glfwSwapBuffers(window); 533 | usleep(16000); 534 | } 535 | quit=true; 536 | midithread.join(); 537 | Pm_Terminate(); 538 | 539 | // Cleanup 540 | ImGui_ImplGlfw_Shutdown(); 541 | ImGui::DestroyContext(); 542 | glfwTerminate(); 543 | 544 | return 0; 545 | } 546 | 547 | ////////////////////////////// simple ini parser 548 | const char *settings=0; 549 | void LoadIniSettings() { 550 | settings=""; 551 | FILE *f=fopen(inifile,"rb"); 552 | if (!f) return ; 553 | fseek(f,0,SEEK_END); 554 | int l=ftell(f); 555 | char *dst=(char*)calloc(1,l+4); 556 | settings=dst; 557 | fseek(f,0,SEEK_SET); 558 | while (!feof(f)) { 559 | char *odst=dst; 560 | char linebuf[256]={}; 561 | if (!fgets(linebuf,255,f)) break; 562 | const char *src=linebuf; 563 | while (*src && isspace(*src)) ++src; // skip white space 564 | if (*src=='#') continue; // comment 565 | const char *ks=dst; 566 | while (*src && !isspace(*src) && *src!='=') *dst++=*src++; // copy key 567 | if (dst==ks) {dst=odst; continue; } // empty key 568 | *dst++=0; // null terminate key 569 | while (*src && isspace(*src)) ++src; // skip white space after key 570 | if (*src++!='=') {dst=odst; continue; } // no equals sign 571 | while (*src && isspace(*src)) ++src; // skip white space after = 572 | const char *vs=dst; 573 | while (*src && *src!='\r' && *src!='\n' && *src!='#') *dst++=*src++; // copy value to end of line 574 | while (dst>vs && isspace(dst[-1])) --dst; // trim trailing spaces 575 | *dst++=0; // null terminate value 576 | } 577 | fclose(f); 578 | *dst++=0; *dst++=0; // empty string to terminate settings 579 | } 580 | const char *GetIniSetting(const char *key, const char *default_value) { 581 | if (!settings) LoadIniSettings(); 582 | for (const char *s=settings;*s;) { 583 | const char *val=s+strlen(s)+1; 584 | if (strcmp(s,key)==0) return val; 585 | s=val+strlen(val)+1; 586 | } 587 | return default_value; 588 | } 589 | void SaveIniSettings() { 590 | if (inmin>inmax) inmin=inmax; 591 | FILE *f=fopen(inifile,"wb"); 592 | if (!f) { 593 | printf("failed to open midi2osc.ini\n"); 594 | return ; 595 | } 596 | fprintf(f,"IP=%s\n",kitname); 597 | fprintf(f,"Device=%s\n",(curport>=0)?devices[curport].name:""); 598 | fprintf(f,"MidiChannelInMin=%d\n",inmin); 599 | fprintf(f,"MidiChannelInMax=%d\n",inmax); 600 | fprintf(f,"MidiChannelOut=%d\n",outchan); 601 | fprintf(f,"SustainMode=%d\n",sustainmode); 602 | fprintf(f,"Spicy=%d\n",ccspicy); 603 | fprintf(f,"X=%d\n",ccx); 604 | fprintf(f,"Y=%d\n",ccy); 605 | fprintf(f,"TopNote=%d\n",topnote); 606 | fprintf(f,"VelocitySens=%d\n",int(velocitysens)); 607 | fprintf(f,"Velocity2Spicy=%d\n",int(velocity2spicy)); 608 | fprintf(f,"Aftertouch2SpicyEnable=%d\n",int(aftertouch2spicy)); 609 | fclose(f); 610 | } 611 | #ifdef _WIN32 612 | int CALLBACK WinMain( 613 | HINSTANCE hInstance, 614 | HINSTANCE hPrevInstance, 615 | LPSTR lpCmdLine, 616 | int nCmdShow 617 | ) { 618 | WSADATA wsaData; 619 | WSAStartup(MAKEWORD(2, 2), &wsaData); 620 | return main(0,0); 621 | } 622 | #endif 623 | -------------------------------------------------------------------------------- /midi2osc.app/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleGetInfoString 6 | MIDI2Osc 7 | CFBundleExecutable 8 | midi2osc 9 | CFBundleIdentifier 10 | com.mediamolecule.www 11 | CFBundleName 12 | midi2osc 13 | CFBundleIconFile 14 | midi2osc.icns 15 | CFBundleShortVersionString 16 | 0.01 17 | CFBundleInfoDictionaryVersion 18 | 6.0 19 | CFBundlePackageType 20 | APPL 21 | IFMajorVersion 22 | 0 23 | IFMinorVersion 24 | 1 25 | 26 | -------------------------------------------------------------------------------- /midi2osc.app/Contents/MacOS/libglfw3.3.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmalex/midi2osc/4d883b84de0047365fd8d61fdaae5de5ac63cc57/midi2osc.app/Contents/MacOS/libglfw3.3.dylib -------------------------------------------------------------------------------- /midi2osc.app/Contents/MacOS/libportmidi.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmalex/midi2osc/4d883b84de0047365fd8d61fdaae5de5ac63cc57/midi2osc.app/Contents/MacOS/libportmidi.dylib -------------------------------------------------------------------------------- /midi2osc.app/Contents/MacOS/midi2osc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmalex/midi2osc/4d883b84de0047365fd8d61fdaae5de5ac63cc57/midi2osc.app/Contents/MacOS/midi2osc -------------------------------------------------------------------------------- /midi2osc.app/Contents/Resources/imgui.ini: -------------------------------------------------------------------------------- 1 | [Window][Debug##Default] 2 | Pos=60,60 3 | Size=400,400 4 | Collapsed=0 5 | 6 | [Window][midi2osc] 7 | Pos=0,0 8 | Size=640,320 9 | Collapsed=0 10 | 11 | -------------------------------------------------------------------------------- /midi2osc.app/Contents/Resources/midi2osc.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmalex/midi2osc/4d883b84de0047365fd8d61fdaae5de5ac63cc57/midi2osc.app/Contents/Resources/midi2osc.icns -------------------------------------------------------------------------------- /midi2osc2.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "midi2osc2", "midi2osc2.vcxproj", "{6965BC55-374F-4710-A326-59A4FB0AFEE5}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {6965BC55-374F-4710-A326-59A4FB0AFEE5}.Debug|x64.ActiveCfg = Debug|x64 17 | {6965BC55-374F-4710-A326-59A4FB0AFEE5}.Debug|x64.Build.0 = Debug|x64 18 | {6965BC55-374F-4710-A326-59A4FB0AFEE5}.Debug|x86.ActiveCfg = Debug|Win32 19 | {6965BC55-374F-4710-A326-59A4FB0AFEE5}.Debug|x86.Build.0 = Debug|Win32 20 | {6965BC55-374F-4710-A326-59A4FB0AFEE5}.Release|x64.ActiveCfg = Release|x64 21 | {6965BC55-374F-4710-A326-59A4FB0AFEE5}.Release|x64.Build.0 = Release|x64 22 | {6965BC55-374F-4710-A326-59A4FB0AFEE5}.Release|x86.ActiveCfg = Release|Win32 23 | {6965BC55-374F-4710-A326-59A4FB0AFEE5}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /midi2osc2.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {6965BC55-374F-4710-A326-59A4FB0AFEE5} 23 | Win32Proj 24 | midi2osc2 25 | 8.1 26 | 27 | 28 | 29 | Application 30 | true 31 | v142 32 | MultiByte 33 | 34 | 35 | Application 36 | false 37 | v142 38 | true 39 | MultiByte 40 | 41 | 42 | Application 43 | true 44 | v142 45 | Unicode 46 | 47 | 48 | Application 49 | false 50 | v142 51 | true 52 | Unicode 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | true 74 | 75 | 76 | true 77 | 78 | 79 | false 80 | 81 | 82 | false 83 | 84 | 85 | 86 | 87 | 88 | Level3 89 | Disabled 90 | WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) 91 | imgui;imgui/examples/libs/gl3w;imgui/examples/libs/glfw/include;portmidi_win 92 | 93 | 94 | Windows 95 | true 96 | msvcrt.lib; libcmt.lib 97 | 98 | 99 | 100 | 101 | 102 | 103 | Level3 104 | Disabled 105 | _DEBUG;_WINDOWS;%(PreprocessorDefinitions) 106 | imgui;imgui/examples/libs/gl3w;imgui/examples/libs/glfw/include;portmidi_win 107 | 108 | 109 | Windows 110 | true 111 | 112 | 113 | 114 | 115 | Level3 116 | 117 | 118 | MaxSpeed 119 | true 120 | true 121 | WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 122 | imgui;imgui/examples/libs/gl3w;imgui/examples/libs/glfw/include;portmidi_win 123 | 124 | 125 | Windows 126 | true 127 | true 128 | true 129 | 130 | 131 | 132 | 133 | Level3 134 | 135 | 136 | MaxSpeed 137 | true 138 | true 139 | NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 140 | imgui;imgui/examples/libs/gl3w;imgui/examples/libs/glfw/include;portmidi_win 141 | 142 | 143 | Windows 144 | true 145 | true 146 | true 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /midi2osc2.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files 41 | 42 | 43 | Source Files 44 | 45 | 46 | Source Files 47 | 48 | 49 | Source Files 50 | 51 | 52 | 53 | 54 | Source Files 55 | 56 | 57 | Source Files 58 | 59 | 60 | Source Files 61 | 62 | 63 | Source Files 64 | 65 | 66 | Source Files 67 | 68 | 69 | Source Files 70 | 71 | 72 | Source Files 73 | 74 | 75 | -------------------------------------------------------------------------------- /midi2oscIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmalex/midi2osc/4d883b84de0047365fd8d61fdaae5de5ac63cc57/midi2oscIcon.png -------------------------------------------------------------------------------- /portmidi_win/portmidi.h: -------------------------------------------------------------------------------- 1 | #ifndef PORT_MIDI_H 2 | #define PORT_MIDI_H 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif /* __cplusplus */ 6 | 7 | /* 8 | * PortMidi Portable Real-Time MIDI Library 9 | * PortMidi API Header File 10 | * Latest version available at: http://sourceforge.net/projects/portmedia 11 | * 12 | * Copyright (c) 1999-2000 Ross Bencina and Phil Burk 13 | * Copyright (c) 2001-2006 Roger B. Dannenberg 14 | * 15 | * Permission is hereby granted, free of charge, to any person obtaining 16 | * a copy of this software and associated documentation files 17 | * (the "Software"), to deal in the Software without restriction, 18 | * including without limitation the rights to use, copy, modify, merge, 19 | * publish, distribute, sublicense, and/or sell copies of the Software, 20 | * and to permit persons to whom the Software is furnished to do so, 21 | * subject to the following conditions: 22 | * 23 | * The above copyright notice and this permission notice shall be 24 | * included in all copies or substantial portions of the Software. 25 | * 26 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 27 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 28 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 29 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 30 | * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 31 | * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 32 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 33 | */ 34 | 35 | /* 36 | * The text above constitutes the entire PortMidi license; however, 37 | * the PortMusic community also makes the following non-binding requests: 38 | * 39 | * Any person wishing to distribute modifications to the Software is 40 | * requested to send the modifications to the original developer so that 41 | * they can be incorporated into the canonical version. It is also 42 | * requested that these non-binding requests be included along with the 43 | * license above. 44 | */ 45 | 46 | /* CHANGELOG FOR PORTMIDI 47 | * (see ../CHANGELOG.txt) 48 | * 49 | * NOTES ON HOST ERROR REPORTING: 50 | * 51 | * PortMidi errors (of type PmError) are generic, system-independent errors. 52 | * When an error does not map to one of the more specific PmErrors, the 53 | * catch-all code pmHostError is returned. This means that PortMidi has 54 | * retained a more specific system-dependent error code. The caller can 55 | * get more information by calling Pm_HasHostError() to test if there is 56 | * a pending host error, and Pm_GetHostErrorText() to get a text string 57 | * describing the error. Host errors are reported on a per-device basis 58 | * because only after you open a device does PortMidi have a place to 59 | * record the host error code. I.e. only 60 | * those routines that receive a (PortMidiStream *) argument check and 61 | * report errors. One exception to this is that Pm_OpenInput() and 62 | * Pm_OpenOutput() can report errors even though when an error occurs, 63 | * there is no PortMidiStream* to hold the error. Fortunately, both 64 | * of these functions return any error immediately, so we do not really 65 | * need per-device error memory. Instead, any host error code is stored 66 | * in a global, pmHostError is returned, and the user can call 67 | * Pm_GetHostErrorText() to get the error message (and the invalid stream 68 | * parameter will be ignored.) The functions 69 | * pm_init and pm_term do not fail or raise 70 | * errors. The job of pm_init is to locate all available devices so that 71 | * the caller can get information via PmDeviceInfo(). If an error occurs, 72 | * the device is simply not listed as available. 73 | * 74 | * Host errors come in two flavors: 75 | * a) host error 76 | * b) host error during callback 77 | * These can occur w/midi input or output devices. (b) can only happen 78 | * asynchronously (during callback routines), whereas (a) only occurs while 79 | * synchronously running PortMidi and any resulting system dependent calls. 80 | * Both (a) and (b) are reported by the next read or write call. You can 81 | * also query for asynchronous errors (b) at any time by calling 82 | * Pm_HasHostError(). 83 | * 84 | * NOTES ON COMPILE-TIME SWITCHES 85 | * 86 | * DEBUG assumes stdio and a console. Use this if you want automatic, simple 87 | * error reporting, e.g. for prototyping. If you are using MFC or some 88 | * other graphical interface with no console, DEBUG probably should be 89 | * undefined. 90 | * PM_CHECK_ERRORS more-or-less takes over error checking for return values, 91 | * stopping your program and printing error messages when an error 92 | * occurs. This also uses stdio for console text I/O. 93 | */ 94 | 95 | #ifndef WIN32 96 | // Linux and OS X have stdint.h 97 | #include 98 | #else 99 | #ifndef INT32_DEFINED 100 | // rather than having users install a special .h file for windows, 101 | // just put the required definitions inline here. porttime.h uses 102 | // these too, so the definitions are (unfortunately) duplicated there 103 | typedef int int32_t; 104 | typedef unsigned int uint32_t; 105 | #define INT32_DEFINED 106 | #endif 107 | #endif 108 | 109 | #ifdef _WINDLL 110 | #define PMEXPORT __declspec(dllexport) 111 | #else 112 | #define PMEXPORT 113 | #endif 114 | 115 | #ifndef FALSE 116 | #define FALSE 0 117 | #endif 118 | #ifndef TRUE 119 | #define TRUE 1 120 | #endif 121 | 122 | /* default size of buffers for sysex transmission: */ 123 | #define PM_DEFAULT_SYSEX_BUFFER_SIZE 1024 124 | 125 | /** List of portmidi errors.*/ 126 | typedef enum { 127 | pmNoError = 0, 128 | pmNoData = 0, /**< A "no error" return that also indicates no data avail. */ 129 | pmGotData = 1, /**< A "no error" return that also indicates data available */ 130 | pmHostError = -10000, 131 | pmInvalidDeviceId, /** out of range or 132 | * output device when input is requested or 133 | * input device when output is requested or 134 | * device is already opened 135 | */ 136 | pmInsufficientMemory, 137 | pmBufferTooSmall, 138 | pmBufferOverflow, 139 | pmBadPtr, /* PortMidiStream parameter is NULL or 140 | * stream is not opened or 141 | * stream is output when input is required or 142 | * stream is input when output is required */ 143 | pmBadData, /** illegal midi data, e.g. missing EOX */ 144 | pmInternalError, 145 | pmBufferMaxSize /** buffer is already as large as it can be */ 146 | /* NOTE: If you add a new error type, be sure to update Pm_GetErrorText() */ 147 | } PmError; 148 | 149 | /** 150 | Pm_Initialize() is the library initialisation function - call this before 151 | using the library. 152 | */ 153 | PMEXPORT PmError Pm_Initialize( void ); 154 | 155 | /** 156 | Pm_Terminate() is the library termination function - call this after 157 | using the library. 158 | */ 159 | PMEXPORT PmError Pm_Terminate( void ); 160 | 161 | /** A single PortMidiStream is a descriptor for an open MIDI device. 162 | */ 163 | typedef void PortMidiStream; 164 | #define PmStream PortMidiStream 165 | 166 | /** 167 | Test whether stream has a pending host error. Normally, the client finds 168 | out about errors through returned error codes, but some errors can occur 169 | asynchronously where the client does not 170 | explicitly call a function, and therefore cannot receive an error code. 171 | The client can test for a pending error using Pm_HasHostError(). If true, 172 | the error can be accessed and cleared by calling Pm_GetErrorText(). 173 | Errors are also cleared by calling other functions that can return 174 | errors, e.g. Pm_OpenInput(), Pm_OpenOutput(), Pm_Read(), Pm_Write(). The 175 | client does not need to call Pm_HasHostError(). Any pending error will be 176 | reported the next time the client performs an explicit function call on 177 | the stream, e.g. an input or output operation. Until the error is cleared, 178 | no new error codes will be obtained, even for a different stream. 179 | */ 180 | PMEXPORT int Pm_HasHostError( PortMidiStream * stream ); 181 | 182 | 183 | /** Translate portmidi error number into human readable message. 184 | These strings are constants (set at compile time) so client has 185 | no need to allocate storage 186 | */ 187 | PMEXPORT const char *Pm_GetErrorText( PmError errnum ); 188 | 189 | /** Translate portmidi host error into human readable message. 190 | These strings are computed at run time, so client has to allocate storage. 191 | After this routine executes, the host error is cleared. 192 | */ 193 | PMEXPORT void Pm_GetHostErrorText(char * msg, unsigned int len); 194 | 195 | #define HDRLENGTH 50 196 | #define PM_HOST_ERROR_MSG_LEN 256u /* any host error msg will occupy less 197 | than this number of characters */ 198 | 199 | /** 200 | Device enumeration mechanism. 201 | 202 | Device ids range from 0 to Pm_CountDevices()-1. 203 | 204 | */ 205 | typedef int PmDeviceID; 206 | #define pmNoDevice -1 207 | typedef struct { 208 | int structVersion; /**< this internal structure version */ 209 | const char *interf; /**< underlying MIDI API, e.g. MMSystem or DirectX */ 210 | const char *name; /**< device name, e.g. USB MidiSport 1x1 */ 211 | int input; /**< true iff input is available */ 212 | int output; /**< true iff output is available */ 213 | int opened; /**< used by generic PortMidi code to do error checking on arguments */ 214 | 215 | } PmDeviceInfo; 216 | 217 | /** Get devices count, ids range from 0 to Pm_CountDevices()-1. */ 218 | PMEXPORT int Pm_CountDevices( void ); 219 | /** 220 | Pm_GetDefaultInputDeviceID(), Pm_GetDefaultOutputDeviceID() 221 | 222 | Return the default device ID or pmNoDevice if there are no devices. 223 | The result (but not pmNoDevice) can be passed to Pm_OpenMidi(). 224 | 225 | The default device can be specified using a small application 226 | named pmdefaults that is part of the PortMidi distribution. This 227 | program in turn uses the Java Preferences object created by 228 | java.util.prefs.Preferences.userRoot().node("/PortMidi"); the 229 | preference is set by calling 230 | prefs.put("PM_RECOMMENDED_OUTPUT_DEVICE", prefName); 231 | or prefs.put("PM_RECOMMENDED_INPUT_DEVICE", prefName); 232 | 233 | In the statements above, prefName is a string describing the 234 | MIDI device in the form "interf, name" where interf identifies 235 | the underlying software system or API used by PortMdi to access 236 | devices and name is the name of the device. These correspond to 237 | the interf and name fields of a PmDeviceInfo. (Currently supported 238 | interfaces are "MMSystem" for Win32, "ALSA" for Linux, and 239 | "CoreMIDI" for OS X, so in fact, there is no choice of interface.) 240 | In "interf, name", the strings are actually substrings of 241 | the full interface and name strings. For example, the preference 242 | "Core, Sport" will match a device with interface "CoreMIDI" 243 | and name "In USB MidiSport 1x1". It will also match "CoreMIDI" 244 | and "In USB MidiSport 2x2". The devices are enumerated in device 245 | ID order, so the lowest device ID that matches the pattern becomes 246 | the default device. Finally, if the comma-space (", ") separator 247 | between interface and name parts of the preference is not found, 248 | the entire preference string is interpreted as a name, and the 249 | interface part is the empty string, which matches anything. 250 | 251 | On the MAC, preferences are stored in 252 | /Users/$NAME/Library/Preferences/com.apple.java.util.prefs.plist 253 | which is a binary file. In addition to the pmdefaults program, 254 | there are utilities that can read and edit this preference file. 255 | 256 | On the PC, 257 | 258 | On Linux, 259 | 260 | */ 261 | PMEXPORT PmDeviceID Pm_GetDefaultInputDeviceID( void ); 262 | /** see PmDeviceID Pm_GetDefaultInputDeviceID() */ 263 | PMEXPORT PmDeviceID Pm_GetDefaultOutputDeviceID( void ); 264 | 265 | /** 266 | PmTimestamp is used to represent a millisecond clock with arbitrary 267 | start time. The type is used for all MIDI timestampes and clocks. 268 | */ 269 | typedef int32_t PmTimestamp; 270 | typedef PmTimestamp (*PmTimeProcPtr)(void *time_info); 271 | 272 | /** TRUE if t1 before t2 */ 273 | #define PmBefore(t1,t2) ((t1-t2) < 0) 274 | /** 275 | \defgroup grp_device Input/Output Devices Handling 276 | @{ 277 | */ 278 | /** 279 | Pm_GetDeviceInfo() returns a pointer to a PmDeviceInfo structure 280 | referring to the device specified by id. 281 | If id is out of range the function returns NULL. 282 | 283 | The returned structure is owned by the PortMidi implementation and must 284 | not be manipulated or freed. The pointer is guaranteed to be valid 285 | between calls to Pm_Initialize() and Pm_Terminate(). 286 | */ 287 | PMEXPORT const PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id ); 288 | 289 | /** 290 | Pm_OpenInput() and Pm_OpenOutput() open devices. 291 | 292 | stream is the address of a PortMidiStream pointer which will receive 293 | a pointer to the newly opened stream. 294 | 295 | inputDevice is the id of the device used for input (see PmDeviceID above). 296 | 297 | inputDriverInfo is a pointer to an optional driver specific data structure 298 | containing additional information for device setup or handle processing. 299 | inputDriverInfo is never required for correct operation. If not used 300 | inputDriverInfo should be NULL. 301 | 302 | outputDevice is the id of the device used for output (see PmDeviceID above.) 303 | 304 | outputDriverInfo is a pointer to an optional driver specific data structure 305 | containing additional information for device setup or handle processing. 306 | outputDriverInfo is never required for correct operation. If not used 307 | outputDriverInfo should be NULL. 308 | 309 | For input, the buffersize specifies the number of input events to be 310 | buffered waiting to be read using Pm_Read(). For output, buffersize 311 | specifies the number of output events to be buffered waiting for output. 312 | (In some cases -- see below -- PortMidi does not buffer output at all 313 | and merely passes data to a lower-level API, in which case buffersize 314 | is ignored.) 315 | 316 | latency is the delay in milliseconds applied to timestamps to determine 317 | when the output should actually occur. (If latency is < 0, 0 is assumed.) 318 | If latency is zero, timestamps are ignored and all output is delivered 319 | immediately. If latency is greater than zero, output is delayed until the 320 | message timestamp plus the latency. (NOTE: the time is measured relative 321 | to the time source indicated by time_proc. Timestamps are absolute, 322 | not relative delays or offsets.) In some cases, PortMidi can obtain 323 | better timing than your application by passing timestamps along to the 324 | device driver or hardware. Latency may also help you to synchronize midi 325 | data to audio data by matching midi latency to the audio buffer latency. 326 | 327 | time_proc is a pointer to a procedure that returns time in milliseconds. It 328 | may be NULL, in which case a default millisecond timebase (PortTime) is 329 | used. If the application wants to use PortTime, it should start the timer 330 | (call Pt_Start) before calling Pm_OpenInput or Pm_OpenOutput. If the 331 | application tries to start the timer *after* Pm_OpenInput or Pm_OpenOutput, 332 | it may get a ptAlreadyStarted error from Pt_Start, and the application's 333 | preferred time resolution and callback function will be ignored. 334 | time_proc result values are appended to incoming MIDI data, and time_proc 335 | times are used to schedule outgoing MIDI data (when latency is non-zero). 336 | 337 | time_info is a pointer passed to time_proc. 338 | 339 | Example: If I provide a timestamp of 5000, latency is 1, and time_proc 340 | returns 4990, then the desired output time will be when time_proc returns 341 | timestamp+latency = 5001. This will be 5001-4990 = 11ms from now. 342 | 343 | return value: 344 | Upon success Pm_Open() returns PmNoError and places a pointer to a 345 | valid PortMidiStream in the stream argument. 346 | If a call to Pm_Open() fails a nonzero error code is returned (see 347 | PMError above) and the value of port is invalid. 348 | 349 | Any stream that is successfully opened should eventually be closed 350 | by calling Pm_Close(). 351 | 352 | */ 353 | PMEXPORT PmError Pm_OpenInput( PortMidiStream** stream, 354 | PmDeviceID inputDevice, 355 | void *inputDriverInfo, 356 | int32_t bufferSize, 357 | PmTimeProcPtr time_proc, 358 | void *time_info ); 359 | 360 | PMEXPORT PmError Pm_OpenOutput( PortMidiStream** stream, 361 | PmDeviceID outputDevice, 362 | void *outputDriverInfo, 363 | int32_t bufferSize, 364 | PmTimeProcPtr time_proc, 365 | void *time_info, 366 | int32_t latency ); 367 | /** @} */ 368 | 369 | /** 370 | \defgroup grp_events_filters Events and Filters Handling 371 | @{ 372 | */ 373 | 374 | /* \function PmError Pm_SetFilter( PortMidiStream* stream, int32_t filters ) 375 | Pm_SetFilter() sets filters on an open input stream to drop selected 376 | input types. By default, only active sensing messages are filtered. 377 | To prohibit, say, active sensing and sysex messages, call 378 | Pm_SetFilter(stream, PM_FILT_ACTIVE | PM_FILT_SYSEX); 379 | 380 | Filtering is useful when midi routing or midi thru functionality is being 381 | provided by the user application. 382 | For example, you may want to exclude timing messages (clock, MTC, start/stop/continue), 383 | while allowing note-related messages to pass. 384 | Or you may be using a sequencer or drum-machine for MIDI clock information but want to 385 | exclude any notes it may play. 386 | */ 387 | 388 | /* Filter bit-mask definitions */ 389 | /** filter active sensing messages (0xFE): */ 390 | #define PM_FILT_ACTIVE (1 << 0x0E) 391 | /** filter system exclusive messages (0xF0): */ 392 | #define PM_FILT_SYSEX (1 << 0x00) 393 | /** filter MIDI clock message (0xF8) */ 394 | #define PM_FILT_CLOCK (1 << 0x08) 395 | /** filter play messages (start 0xFA, stop 0xFC, continue 0xFB) */ 396 | #define PM_FILT_PLAY ((1 << 0x0A) | (1 << 0x0C) | (1 << 0x0B)) 397 | /** filter tick messages (0xF9) */ 398 | #define PM_FILT_TICK (1 << 0x09) 399 | /** filter undefined FD messages */ 400 | #define PM_FILT_FD (1 << 0x0D) 401 | /** filter undefined real-time messages */ 402 | #define PM_FILT_UNDEFINED PM_FILT_FD 403 | /** filter reset messages (0xFF) */ 404 | #define PM_FILT_RESET (1 << 0x0F) 405 | /** filter all real-time messages */ 406 | #define PM_FILT_REALTIME (PM_FILT_ACTIVE | PM_FILT_SYSEX | PM_FILT_CLOCK | \ 407 | PM_FILT_PLAY | PM_FILT_UNDEFINED | PM_FILT_RESET | PM_FILT_TICK) 408 | /** filter note-on and note-off (0x90-0x9F and 0x80-0x8F */ 409 | #define PM_FILT_NOTE ((1 << 0x19) | (1 << 0x18)) 410 | /** filter channel aftertouch (most midi controllers use this) (0xD0-0xDF)*/ 411 | #define PM_FILT_CHANNEL_AFTERTOUCH (1 << 0x1D) 412 | /** per-note aftertouch (0xA0-0xAF) */ 413 | #define PM_FILT_POLY_AFTERTOUCH (1 << 0x1A) 414 | /** filter both channel and poly aftertouch */ 415 | #define PM_FILT_AFTERTOUCH (PM_FILT_CHANNEL_AFTERTOUCH | PM_FILT_POLY_AFTERTOUCH) 416 | /** Program changes (0xC0-0xCF) */ 417 | #define PM_FILT_PROGRAM (1 << 0x1C) 418 | /** Control Changes (CC's) (0xB0-0xBF)*/ 419 | #define PM_FILT_CONTROL (1 << 0x1B) 420 | /** Pitch Bender (0xE0-0xEF*/ 421 | #define PM_FILT_PITCHBEND (1 << 0x1E) 422 | /** MIDI Time Code (0xF1)*/ 423 | #define PM_FILT_MTC (1 << 0x01) 424 | /** Song Position (0xF2) */ 425 | #define PM_FILT_SONG_POSITION (1 << 0x02) 426 | /** Song Select (0xF3)*/ 427 | #define PM_FILT_SONG_SELECT (1 << 0x03) 428 | /** Tuning request (0xF6)*/ 429 | #define PM_FILT_TUNE (1 << 0x06) 430 | /** All System Common messages (mtc, song position, song select, tune request) */ 431 | #define PM_FILT_SYSTEMCOMMON (PM_FILT_MTC | PM_FILT_SONG_POSITION | PM_FILT_SONG_SELECT | PM_FILT_TUNE) 432 | 433 | 434 | PMEXPORT PmError Pm_SetFilter( PortMidiStream* stream, int32_t filters ); 435 | 436 | #define Pm_Channel(channel) (1<<(channel)) 437 | /** 438 | Pm_SetChannelMask() filters incoming messages based on channel. 439 | The mask is a 16-bit bitfield corresponding to appropriate channels. 440 | The Pm_Channel macro can assist in calling this function. 441 | i.e. to set receive only input on channel 1, call with 442 | Pm_SetChannelMask(Pm_Channel(1)); 443 | Multiple channels should be OR'd together, like 444 | Pm_SetChannelMask(Pm_Channel(10) | Pm_Channel(11)) 445 | 446 | Note that channels are numbered 0 to 15 (not 1 to 16). Most 447 | synthesizer and interfaces number channels starting at 1, but 448 | PortMidi numbers channels starting at 0. 449 | 450 | All channels are allowed by default 451 | */ 452 | PMEXPORT PmError Pm_SetChannelMask(PortMidiStream *stream, int mask); 453 | 454 | /** 455 | Pm_Abort() terminates outgoing messages immediately 456 | The caller should immediately close the output port; 457 | this call may result in transmission of a partial midi message. 458 | There is no abort for Midi input because the user can simply 459 | ignore messages in the buffer and close an input device at 460 | any time. 461 | */ 462 | PMEXPORT PmError Pm_Abort( PortMidiStream* stream ); 463 | 464 | /** 465 | Pm_Close() closes a midi stream, flushing any pending buffers. 466 | (PortMidi attempts to close open streams when the application 467 | exits -- this is particularly difficult under Windows.) 468 | */ 469 | PMEXPORT PmError Pm_Close( PortMidiStream* stream ); 470 | 471 | /** 472 | Pm_Synchronize() instructs PortMidi to (re)synchronize to the 473 | time_proc passed when the stream was opened. Typically, this 474 | is used when the stream must be opened before the time_proc 475 | reference is actually advancing. In this case, message timing 476 | may be erratic, but since timestamps of zero mean 477 | "send immediately," initialization messages with zero timestamps 478 | can be written without a functioning time reference and without 479 | problems. Before the first MIDI message with a non-zero 480 | timestamp is written to the stream, the time reference must 481 | begin to advance (for example, if the time_proc computes time 482 | based on audio samples, time might begin to advance when an 483 | audio stream becomes active). After time_proc return values 484 | become valid, and BEFORE writing the first non-zero timestamped 485 | MIDI message, call Pm_Synchronize() so that PortMidi can observe 486 | the difference between the current time_proc value and its 487 | MIDI stream time. 488 | 489 | In the more normal case where time_proc 490 | values advance continuously, there is no need to call 491 | Pm_Synchronize. PortMidi will always synchronize at the 492 | first output message and periodically thereafter. 493 | */ 494 | PmError Pm_Synchronize( PortMidiStream* stream ); 495 | 496 | 497 | /** 498 | Pm_Message() encodes a short Midi message into a 32-bit word. If data1 499 | and/or data2 are not present, use zero. 500 | 501 | Pm_MessageStatus(), Pm_MessageData1(), and 502 | Pm_MessageData2() extract fields from a 32-bit midi message. 503 | */ 504 | #define Pm_Message(status, data1, data2) \ 505 | ((((data2) << 16) & 0xFF0000) | \ 506 | (((data1) << 8) & 0xFF00) | \ 507 | ((status) & 0xFF)) 508 | #define Pm_MessageStatus(msg) ((msg) & 0xFF) 509 | #define Pm_MessageData1(msg) (((msg) >> 8) & 0xFF) 510 | #define Pm_MessageData2(msg) (((msg) >> 16) & 0xFF) 511 | 512 | typedef int32_t PmMessage; /**< see PmEvent */ 513 | /** 514 | All midi data comes in the form of PmEvent structures. A sysex 515 | message is encoded as a sequence of PmEvent structures, with each 516 | structure carrying 4 bytes of the message, i.e. only the first 517 | PmEvent carries the status byte. 518 | 519 | Note that MIDI allows nested messages: the so-called "real-time" MIDI 520 | messages can be inserted into the MIDI byte stream at any location, 521 | including within a sysex message. MIDI real-time messages are one-byte 522 | messages used mainly for timing (see the MIDI spec). PortMidi retains 523 | the order of non-real-time MIDI messages on both input and output, but 524 | it does not specify exactly how real-time messages are processed. This 525 | is particulary problematic for MIDI input, because the input parser 526 | must either prepare to buffer an unlimited number of sysex message 527 | bytes or to buffer an unlimited number of real-time messages that 528 | arrive embedded in a long sysex message. To simplify things, the input 529 | parser is allowed to pass real-time MIDI messages embedded within a 530 | sysex message, and it is up to the client to detect, process, and 531 | remove these messages as they arrive. 532 | 533 | When receiving sysex messages, the sysex message is terminated 534 | by either an EOX status byte (anywhere in the 4 byte messages) or 535 | by a non-real-time status byte in the low order byte of the message. 536 | If you get a non-real-time status byte but there was no EOX byte, it 537 | means the sysex message was somehow truncated. This is not 538 | considered an error; e.g., a missing EOX can result from the user 539 | disconnecting a MIDI cable during sysex transmission. 540 | 541 | A real-time message can occur within a sysex message. A real-time 542 | message will always occupy a full PmEvent with the status byte in 543 | the low-order byte of the PmEvent message field. (This implies that 544 | the byte-order of sysex bytes and real-time message bytes may not 545 | be preserved -- for example, if a real-time message arrives after 546 | 3 bytes of a sysex message, the real-time message will be delivered 547 | first. The first word of the sysex message will be delivered only 548 | after the 4th byte arrives, filling the 4-byte PmEvent message field. 549 | 550 | The timestamp field is observed when the output port is opened with 551 | a non-zero latency. A timestamp of zero means "use the current time", 552 | which in turn means to deliver the message with a delay of 553 | latency (the latency parameter used when opening the output port.) 554 | Do not expect PortMidi to sort data according to timestamps -- 555 | messages should be sent in the correct order, and timestamps MUST 556 | be non-decreasing. See also "Example" for Pm_OpenOutput() above. 557 | 558 | A sysex message will generally fill many PmEvent structures. On 559 | output to a PortMidiStream with non-zero latency, the first timestamp 560 | on sysex message data will determine the time to begin sending the 561 | message. PortMidi implementations may ignore timestamps for the 562 | remainder of the sysex message. 563 | 564 | On input, the timestamp ideally denotes the arrival time of the 565 | status byte of the message. The first timestamp on sysex message 566 | data will be valid. Subsequent timestamps may denote 567 | when message bytes were actually received, or they may be simply 568 | copies of the first timestamp. 569 | 570 | Timestamps for nested messages: If a real-time message arrives in 571 | the middle of some other message, it is enqueued immediately with 572 | the timestamp corresponding to its arrival time. The interrupted 573 | non-real-time message or 4-byte packet of sysex data will be enqueued 574 | later. The timestamp of interrupted data will be equal to that of 575 | the interrupting real-time message to insure that timestamps are 576 | non-decreasing. 577 | */ 578 | typedef struct { 579 | PmMessage message; 580 | PmTimestamp timestamp; 581 | } PmEvent; 582 | 583 | /** 584 | @} 585 | */ 586 | /** \defgroup grp_io Reading and Writing Midi Messages 587 | @{ 588 | */ 589 | /** 590 | Pm_Read() retrieves midi data into a buffer, and returns the number 591 | of events read. Result is a non-negative number unless an error occurs, 592 | in which case a PmError value will be returned. 593 | 594 | Buffer Overflow 595 | 596 | The problem: if an input overflow occurs, data will be lost, ultimately 597 | because there is no flow control all the way back to the data source. 598 | When data is lost, the receiver should be notified and some sort of 599 | graceful recovery should take place, e.g. you shouldn't resume receiving 600 | in the middle of a long sysex message. 601 | 602 | With a lock-free fifo, which is pretty much what we're stuck with to 603 | enable portability to the Mac, it's tricky for the producer and consumer 604 | to synchronously reset the buffer and resume normal operation. 605 | 606 | Solution: the buffer managed by PortMidi will be flushed when an overflow 607 | occurs. The consumer (Pm_Read()) gets an error message (pmBufferOverflow) 608 | and ordinary processing resumes as soon as a new message arrives. The 609 | remainder of a partial sysex message is not considered to be a "new 610 | message" and will be flushed as well. 611 | 612 | */ 613 | PMEXPORT int Pm_Read( PortMidiStream *stream, PmEvent *buffer, int32_t length ); 614 | 615 | /** 616 | Pm_Poll() tests whether input is available, 617 | returning TRUE, FALSE, or an error value. 618 | */ 619 | PMEXPORT PmError Pm_Poll( PortMidiStream *stream); 620 | 621 | /** 622 | Pm_Write() writes midi data from a buffer. This may contain: 623 | - short messages 624 | or 625 | - sysex messages that are converted into a sequence of PmEvent 626 | structures, e.g. sending data from a file or forwarding them 627 | from midi input. 628 | 629 | Use Pm_WriteSysEx() to write a sysex message stored as a contiguous 630 | array of bytes. 631 | 632 | Sysex data may contain embedded real-time messages. 633 | */ 634 | PMEXPORT PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, int32_t length ); 635 | 636 | /** 637 | Pm_WriteShort() writes a timestamped non-system-exclusive midi message. 638 | Messages are delivered in order as received, and timestamps must be 639 | non-decreasing. (But timestamps are ignored if the stream was opened 640 | with latency = 0.) 641 | */ 642 | PMEXPORT PmError Pm_WriteShort( PortMidiStream *stream, PmTimestamp when, int32_t msg); 643 | 644 | /** 645 | Pm_WriteSysEx() writes a timestamped system-exclusive midi message. 646 | */ 647 | PMEXPORT PmError Pm_WriteSysEx( PortMidiStream *stream, PmTimestamp when, unsigned char *msg); 648 | 649 | /** @} */ 650 | 651 | #ifdef __cplusplus 652 | } 653 | #endif /* __cplusplus */ 654 | #endif /* PORT_MIDI_H */ 655 | -------------------------------------------------------------------------------- /portmidi_win/portmidi_s.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmalex/midi2osc/4d883b84de0047365fd8d61fdaae5de5ac63cc57/portmidi_win/portmidi_s.lib -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmalex/midi2osc/4d883b84de0047365fd8d61fdaae5de5ac63cc57/screenshot.png --------------------------------------------------------------------------------