├── .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 | 
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"; else *out=devices[inputids[idx-1]].name;
269 | return true;
270 | }
271 | void UpdateIP() {
272 | ScopedLock lock(oscmutex);
273 | const char *portstr=strchr(kitname,':');
274 | strcpy(ip,kitname);
275 | if (portstr) {
276 | port=portstr+1;
277 | ip[portstr-kitname]=0;
278 | } else port="9000";
279 | if (!*port) port="9000";
280 | if (fd>=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
--------------------------------------------------------------------------------