├── .github └── workflows │ └── build-plugin.yml ├── CHANGELOG.md ├── LICENSE-dist.txt ├── LICENSE.txt ├── Makefile ├── README.md ├── docs ├── Oscelot-Ctx.gif ├── Oscelot-Meowmory.gif ├── Oscelot-exp.png ├── Oscelot-expander.gif ├── Oscelot-intro.gif ├── Oscelot-map-select.gif ├── Oscelot-map.gif ├── Oscelot-scan.gif ├── Oscelot-target.png ├── Oscelot.md └── screenshot.png ├── plugin.json ├── presets ├── 8FEB_OpenStageControl.json └── Oscelot_Patch.vcv ├── res ├── OscelotExpander_BlackSteel.svg ├── OscelotExpander_BlueSteel.svg ├── OscelotExpander_Brass.svg ├── OscelotExpander_GunMetal.svg ├── Oscelot_BlackSteel.svg ├── Oscelot_BlueSteel.svg ├── Oscelot_Brass.svg ├── Oscelot_GunMetal.svg ├── components │ ├── Port.svg │ ├── Screw.svg │ ├── SliderHandle.svg │ ├── SliderHorizontal.svg │ ├── paw.svg │ ├── paw0.svg │ └── paw1.svg └── fonts │ ├── NovaMono-Regular.ttf │ └── OFL.txt └── src ├── MapModuleBase.hpp ├── OscController.cpp ├── Oscelot.cpp ├── Oscelot.hpp ├── OscelotExpander.cpp ├── OscelotExpander.hpp ├── components.hpp ├── components ├── LedTextField.hpp ├── MeowMory.hpp ├── OscelotParam.hpp ├── ParamHandleIndicator.hpp └── PawButtons.hpp ├── helpers.hpp ├── osc ├── OscArgs.hpp ├── OscBundle.hpp ├── OscController.hpp ├── OscMessage.hpp ├── OscReceiver.hpp ├── OscSender.hpp └── oscpack │ ├── LICENSE │ ├── README.md │ ├── ip │ ├── IpEndpointName.cpp │ ├── IpEndpointName.h │ ├── NetworkingUtils.h │ ├── PacketListener.h │ ├── TimerListener.h │ ├── UdpSocket.h │ ├── posix │ │ ├── NetworkingUtils.cpp │ │ └── UdpSocket.cpp │ └── win32 │ │ ├── NetworkingUtils.cpp │ │ └── UdpSocket.cpp │ └── osc │ ├── MessageMappingOscPacketListener.h │ ├── OscException.h │ ├── OscHostEndianness.h │ ├── OscOutboundPacketStream.cpp │ ├── OscOutboundPacketStream.h │ ├── OscPacketListener.h │ ├── OscPrintReceivedElements.cpp │ ├── OscPrintReceivedElements.h │ ├── OscReceivedElements.cpp │ ├── OscReceivedElements.h │ ├── OscTypes.cpp │ └── OscTypes.h ├── plugin.cpp ├── plugin.hpp └── ui ├── ParamWidgetContextExtender.hpp └── ThemedModuleWidget.hpp /.github/workflows/build-plugin.yml: -------------------------------------------------------------------------------- 1 | name: "Build VCV Rack Plugin" 2 | 3 | on: 4 | push: 5 | branches: 6 | - "master" 7 | - "*test" 8 | tags: 9 | - v* 10 | pull_request: 11 | branches: 12 | - "master" 13 | 14 | env: 15 | rack-sdk-version: 2.0.0 16 | 17 | defaults: 18 | run: 19 | shell: bash 20 | 21 | jobs: 22 | build: 23 | name: ${{ matrix.config.name }} 24 | runs-on: ${{ matrix.config.os }} 25 | strategy: 26 | matrix: 27 | config: 28 | - { 29 | name: Linux, 30 | arch: lin, 31 | os: ubuntu-latest, 32 | prepare-os: sudo apt-get update && sudo apt install -y libglu-dev 33 | } 34 | - { 35 | name: MacOS, 36 | arch: mac, 37 | os: macos-latest, 38 | prepare-os: "brew install mesa" 39 | } 40 | - { 41 | name: Windows, 42 | arch: win, 43 | os: windows-latest, 44 | prepare-os: export CC=gcc 45 | } 46 | steps: 47 | - uses: actions/checkout@v2 48 | with: 49 | submodules: recursive 50 | - name: Get Rack-SDK 51 | run: | 52 | pushd $HOME 53 | curl -o Rack-SDK.zip https://vcvrack.com/downloads/Rack-SDK-${{ env.rack-sdk-version }}-${{ matrix.config.arch }}.zip 54 | unzip Rack-SDK.zip 55 | - name: Patch plugin.mk, use 7zip on Windows 56 | if: runner.os == 'Windows' 57 | run: | 58 | sed -i 's/zip -q -9 -r/7z a -tzip -mx=9/' $HOME/Rack-SDK/plugin.mk 59 | - name: Modify plugin version 60 | # only modify plugin version if no tag was created 61 | if: "! startsWith(github.ref, 'refs/tags/v')" 62 | run: | 63 | gitrev=`git rev-parse --short HEAD` 64 | pluginversion=`jq -r '.version' plugin.json` 65 | echo "Set plugin version from $pluginversion to $pluginversion-test because no TAG was created (${{github.ref}})" 66 | cat <<< `jq --arg VERSION "$pluginversion-test" '.version=$VERSION' plugin.json` > plugin.json 67 | - name: Build plugin 68 | run: | 69 | ${{ matrix.config.prepare-os }} 70 | export RACK_DIR=$HOME/Rack-SDK 71 | make -j dep 72 | make -j dist 73 | - name: Upload artifact 74 | uses: actions/upload-artifact@v2 75 | with: 76 | path: dist/**/*.vcvplugin 77 | name: ${{ matrix.config.name }} 78 | 79 | pre-release: 80 | name: "Pre Release" 81 | # only create a dev release if no tag was created 82 | if: "! startsWith(github.ref, 'refs/tags/v')" 83 | runs-on: "ubuntu-latest" 84 | needs: build 85 | 86 | steps: 87 | - uses: actions/download-artifact@v2 88 | with: 89 | path: _artifacts 90 | - uses: "marvinpinto/action-automatic-releases@latest" 91 | with: 92 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 93 | automatic_release_tag: "latest" 94 | prerelease: true 95 | title: "Latest Development Build" 96 | files: | 97 | _artifacts/**/*.vcvplugin 98 | 99 | release: 100 | name: "Release" 101 | # only create a release if a tag was created e.g. v1.2.3 102 | if: "startsWith(github.ref, 'refs/tags/v')" 103 | runs-on: "ubuntu-latest" 104 | needs: build 105 | 106 | steps: 107 | - uses: actions/download-artifact@v2 108 | with: 109 | path: _artifacts 110 | - uses: "marvinpinto/action-automatic-releases@latest" 111 | with: 112 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 113 | prerelease: false 114 | files: | 115 | _artifacts/**/*.vcvplugin -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.0.0 2 | - VCV Library Release 3 | 4 | ## 2.0.0 Beta (11/21/2021) 5 | - Added fix for deadlock issue, updated to latest VCV SDK 6 | - Send feedback only if display value of param changes 7 | - Fix headers 8 | 9 | ## 2.0.0 Beta (11/09/2021) 10 | - Fix module browser crash on Mac 11 | - Removed some test code 12 | - Added new skin - Black Steel 13 | 14 | ## 2.0.0 Beta (11/07/2021) 15 | - 2.0 API changes, Code refactor/cleanup 16 | - New Features: 17 | - Reduce OSC feedback, only send full feedback when param changes. Also Added option to always send full feedback. 18 | - MeowMory Banks! Save upto 128 banks of mappings using the slider 19 | 20 | 21 | ## 1.0.2 22 | 23 | - Trigger/CV Expander for OSC'elot 24 | - Fix doc links 25 | 26 | ## 1.0.1 27 | 28 | - First Public release 29 | 30 | ## 1.0.0-alpha 31 | 32 | - Alpha release -------------------------------------------------------------------------------- /LICENSE-dist.txt: -------------------------------------------------------------------------------- 1 | 2 | #### FUNDAMENTAL #### 3 | 4 | Copyright (c) 2016-2018 Andrew Belt 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | 12 | 13 | 14 | 15 | #### AUDIBLE INSTRUMENTS 16 | 17 | Copyright 2016 Andrew Belt 18 | 19 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 20 | 21 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 22 | 23 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 24 | 25 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 26 | 27 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | 30 | 31 | 32 | #### CORE and VCV RACK #### 33 | 34 | All VCV Rack source code is copyright © 2019 Andrew Belt and licensed under the GNU General Public License v3.0 with the "VCV Rack Non-Commercial Plugin License Exception", allowed under section 7 of GPLv3, and a commercial licensing option. 35 | 36 | 37 | #### BACONMUSIC #### 38 | 39 | Copyright © 2017-2019 Paul Walker 40 | Licsened under the Gnu Public License, version 3. https://www.gnu.org/licenses/gpl-3.0.en.html -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | RACK_DIR ?= ../.. 2 | 3 | 4 | include $(RACK_DIR)/arch.mk 5 | 6 | ifdef ARCH_WIN 7 | SOURCES += $(wildcard src/osc/oscpack/ip/win32/*.cpp) 8 | LDFLAGS += -lws2_32 -lwinmm 9 | else 10 | SOURCES += $(wildcard src/osc/oscpack/ip/posix/*.cpp) 11 | endif 12 | 13 | SOURCES += $(wildcard src/osc/oscpack/ip/*.cpp) $(wildcard src/osc/oscpack/osc/*.cpp) 14 | SOURCES += $(wildcard src/*.cpp) 15 | 16 | DISTRIBUTABLES += $(wildcard LICENSE*) res presets 17 | 18 | include $(RACK_DIR)/plugin.mk -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OSC'elot 2 | 3 | 4 | ![Version](https://img.shields.io/badge/version-2.0.0-green.svg?style=flat-square) 5 | ![Rack SDK](https://img.shields.io/badge/Rack--SDK-2.0.0-red.svg?style=flat-square) 6 | ![License](https://img.shields.io/badge/license-GPLv3-blue.svg?style=flat-square) 7 | ![Language](https://img.shields.io/badge/language-C++-yellow.svg?style=flat-square) 8 | 9 | [OSC'elot Manual](./docs/Oscelot.md): Map VCV Rack parameters to OSC controllers with OSC feedback. 10 | 11 | ![Intro image](./docs/screenshot.png) 12 | 13 | Feel free to contact me or create a GitHub issue if you have any problems or questions! 14 | 15 | If you like my work consider donating to https://paypal.me/themodularmind. Thank you! 16 | 17 | ## Special thanks 18 | 19 | - [Stoermelder](https://library.vcvrack.com/?brand=stoermelder) for all his awesome modules, especially MIDI-CAT on which OSC'elot is based. 20 | - [Omri Cohen](https://www.youtube.com/channel/UCuWKHSHTHMV_nVSeNH4gYAg) for his video tutorials that got me into into VCV Rack. 21 | - [VCV Rack](https://vcvrack.com/) Andrew Belt for creating VCV Rack 22 | 23 | ## License 24 | 25 | All **source code** is copyright © 2021 and is licensed under the [GNU General Public License, version v3.0](./LICENSE.txt). 26 | 27 | All **files** and **graphics** in the `res` and `res-src` directories are licensed under [CC BY-NC-ND 4.0](https://creativecommons.org/licenses/by-nc-nd/4.0/). You may not distribute modified adaptations of these graphics. -------------------------------------------------------------------------------- /docs/Oscelot-Ctx.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/The-Modular-Mind/oscelot/81c5b530bb39a9405714a974ac5d07241e303d18/docs/Oscelot-Ctx.gif -------------------------------------------------------------------------------- /docs/Oscelot-Meowmory.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/The-Modular-Mind/oscelot/81c5b530bb39a9405714a974ac5d07241e303d18/docs/Oscelot-Meowmory.gif -------------------------------------------------------------------------------- /docs/Oscelot-exp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/The-Modular-Mind/oscelot/81c5b530bb39a9405714a974ac5d07241e303d18/docs/Oscelot-exp.png -------------------------------------------------------------------------------- /docs/Oscelot-expander.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/The-Modular-Mind/oscelot/81c5b530bb39a9405714a974ac5d07241e303d18/docs/Oscelot-expander.gif -------------------------------------------------------------------------------- /docs/Oscelot-intro.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/The-Modular-Mind/oscelot/81c5b530bb39a9405714a974ac5d07241e303d18/docs/Oscelot-intro.gif -------------------------------------------------------------------------------- /docs/Oscelot-map-select.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/The-Modular-Mind/oscelot/81c5b530bb39a9405714a974ac5d07241e303d18/docs/Oscelot-map-select.gif -------------------------------------------------------------------------------- /docs/Oscelot-map.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/The-Modular-Mind/oscelot/81c5b530bb39a9405714a974ac5d07241e303d18/docs/Oscelot-map.gif -------------------------------------------------------------------------------- /docs/Oscelot-scan.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/The-Modular-Mind/oscelot/81c5b530bb39a9405714a974ac5d07241e303d18/docs/Oscelot-scan.gif -------------------------------------------------------------------------------- /docs/Oscelot-target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/The-Modular-Mind/oscelot/81c5b530bb39a9405714a974ac5d07241e303d18/docs/Oscelot-target.png -------------------------------------------------------------------------------- /docs/Oscelot.md: -------------------------------------------------------------------------------- 1 | # OSC'elot 2 | 3 | OSC'elot is a mapping module for OSC controllers based on stoermelder's MIDI-CAT. 4 | 5 | ![OSC'elot intro](./Oscelot-intro.gif) 6 | 7 |
8 | 9 | * [Controller Types](#controller-types) 10 | + [Faders](#faders) 11 | + [Encoders](#encoders) 12 | + [Buttons](#buttons) 13 | * [OSC Feedback](#osc-feedback) 14 | * [Mapping parameters](#mapping-parameters) 15 | + [Map an entire module](#map-an-entire-module) 16 | + [Map parameters one at a time](#map-parameters-one-at-a-time) 17 | * [MeowMory](#meowmory) 18 | * [Context-Label](#context-label) 19 | * [Menu Options](#menu-options) 20 | + [Additional features](#additional-features) 21 | * [Controller Modes](#controller-modes) 22 | * [Expander](#expander) 23 | 24 |
25 | 26 | --- 27 | ## Controller Types 28 | It currently supports mapping of Faders, Encoders and Buttons. 29 | 30 | ### Faders 31 | - OSC Messages **must** have an address ending with `/fader` 32 | - It **must** have two arguments, Id (Integer) and Value(Float between 0-1) 33 | - > `/fader, args: (1, 0.5573)` 34 | 35 | ### Encoders 36 | - OSC Messages **must** have an address ending with `/encoder` 37 | - It **must** have two arguments, Id (Integer) and Delta Value(multiples of -1.0 or +1.0) 38 | - Encoders have default sensitivity set to *649* 39 | - > `/encoder, args: (1, -1.0)` 40 | 41 | ### Buttons 42 | - OSC Messages **must** have an address ending with `/button` 43 | - It **must** have two arguments, Id (Integer) and Value(0.0 or 1.0) 44 | - > `/button, args: (1, 1.0)` 45 | 46 |
47 | 48 | --- 49 | ## OSC Feedback 50 | If the Sender is activated, any parameter change will generate two OSC messages as feedback. On activation of the Sender it also sends these two messages for all currently mapped controls. This is useful for initialization of the controls on the OSC controller. 51 | 52 | For a fader mapped to a MixMaster Volume fader: 53 | > Sent from controller: 54 | `/fader/1/0.3499999940395355` 55 | 56 | > Sent from OSC'elot: 57 | `/fader, args: (1, 0.3499999940395355)` 58 | `/fader/info, args: (1, 'MixMaster', '-01-: level', '-21.335', ' dB')` 59 | 60 | For an encoder mapped to a MixMaster Pan knob: 61 | > Sent from controller: 62 | `/encoder/1/1.0` 63 | 64 | > Sent from OSC'elot: 65 | `/encoder, args: (1, 0.5231125950813293)` 66 | `/encoder/info, args: (1, 'MixMaster', '-01-: pan', '4.6225', '%')` 67 | 68 | The first message contains the id and the current value of the mapped param. (0.0-1.0) 69 | The second message ending with `/info` has the integer Id arg followed by info about the mapped param: 70 | | Name | Type | Value | Notes | 71 | | ------------- |:---------:|:-------------:|-------------------------------------------| 72 | | Id | Integer | `1` | Id of mapped OSC controller | 73 | | ModuleName | String | `'MixMaster'` | Not affected by OSC'elot labels | 74 | | Label | String | `'-01-: pan'` | Not affected by OSC'elot labels | 75 | | DisplayValue | String | `'4.6225'` | Value shown when for param in VCV | 76 | | Unit | String | `'%'` | Blank string if param does not have units | 77 | 78 |
79 | 80 | --- 81 | ## Mapping parameters 82 | A typical workflow for mapping your controller will look like this: 83 | 84 | - Connect your OSC controller, whether physical/virtual by setting the receive port and starting the Receiver. 85 | - If your controller can receive OSC messages you can set the send port and start the Sender. 86 | 87 |
88 | 89 | ### Map an entire module 90 | This option changes your cursor into a crosshair which needs to be pointed onto any module within your patch by clicking on the panel. 91 | - *`Clear first`* clears OSC mappings before mapping new module. **SHORTCUT** `Ctrl/Cmd+Shift+D` 92 | - *`Keep OSC assignments`* keeps the OSC mappings and re-maps them onto the new module. **SHORTCUT** `Shift+D` 93 | 94 | ![OSC'elot module select](./Oscelot-map-select.gif) 95 | 96 |
97 | 98 | ### Map parameters one at a time 99 | - Activate the first mapping slot by clicking on it. 100 | - Click on a parameter of any module in your patch. The slot will bind this parameter. 101 | - Touch a control or key on your OSC device. The slot will bind the OSC message. 102 | - Repeat this process until all the desired parameters have been mapped. 103 | 104 | A blinking mapping indicator will indicate the bound parameter the mapping-slot which is currently selected. 105 | 106 | ![OSC'elot mapping](./Oscelot-map.gif) 107 | 108 |
109 | 110 | --- 111 | ## MeowMory 112 | OSC'elot allows you store an unlimited number of module-specific mappings which can be recalled for the same type of module without doing any mapping manually. 113 | A typical workflow will look like this: 114 | 115 | - Create a mapping using your OSC device of any module in OSC'elot. 116 | - Under the `MeowMory` section you have options to *`Store mapping`*. 117 | - The saved module mappings are listed under *`Available mappings`*. 118 | - You can store only one mapping of any specific module-type. If you store a mapping for a module which has one already it will be replaced. 119 | - You can remove any mapping using the the context menu *`Delete`*. Intializing the module does not remove them. 120 | 121 | Stored module-mappings can be recalled by using the middle `Apply` button or hotkey `Shift+V` while hovering OSC'elot. 122 | The cursor changes to a crosshair and the saved OSC-mapping is loaded into OSC'elot after you click on a module in your patch. 123 | 124 | ![MeowMory workflow1](./Oscelot-Meowmory.gif) 125 | 126 |
127 | 128 | The `Prev` and `Next` buttons scan your patch from top-left to bottom-right and apply a stored mapping to the next/previous module of the current mapped module. 129 | Modules without a mapping will be skipped. This can also be triggered via OSC: 130 | > `/oscelot/next` 131 | > `/oscelot/prev` 132 | 133 | ![MeowMory workflow2](./Oscelot-scan.gif) 134 | 135 |
136 | 137 | --- 138 | ## Context-Label 139 | After a parameter has been mapped the parameter's context menu is extended with some addtional menu items allowing quick OSC learning and centering it's OSC'elot module on the center of the screen. 140 | 141 | ![OSC'elot parameter's context menu](./Oscelot-target.png) 142 | 143 | There are even further options when you set a `Context label`. This allows you to name each instance of OSC'elot in your patch. This name can be addressed in any parameter's context menu for activating OSC mapping or re-mapping parameters to an existing OSC control. 144 | 145 | ![CTX workflow](./Oscelot-Ctx.gif) 146 | 147 |
148 | 149 | --- 150 | ## Menu Options 151 | *`Re-send OSC feedback`* allows you to manually send feedback for all mapped parameters back to your OSC device. 152 | - The *`Now`* option can be useful if you switch/restart your OSC device or the device needs to be initalized again. 153 | - The *`Periodically`* option when enabled sends OSC feedback **once a second** for all mapped controls regardless of whether the parameter has changed. 154 | 155 | *`Locate and indicate`*: 156 | Received OSC messages have no effect on the mapped parameters, instead the module is centered on the screen and the parameter mapping indicator flashes for a short period of time. When finished verifying all OSC controls switch back to *`Operating`* mode for normal operation of OSC'elot. 157 | 158 | *`Lock mapping slots`*: 159 | Accidental changes of the mapping slots can be prevented by using this option, which locks access to the mapping slots. 160 | - Scrolling Rack's current view by mouse is interrupted by OSC'elot's list widget while hovered. As this behavior can be annoying all scrolling events are ignored if _Lock mapping slots_ is enabled. 161 | 162 | *`Ignore OSC devices`*: 163 | Skips any OSC device settings when loading a preset into OSC'elot, only mapping slots will be loaded. 164 | 165 | ### Additional features 166 | 167 | - The text shown in every mapping slot can be replaced by a custom text label in the context menu. It's prefilled by default with the automatic label. 168 | - If you find the blue mapping indicators distracting you can disable them in OSC'elot's context menu. 169 | - An active mapping process can be aborted by hitting the `ESC`-key while hovering the mouse over OSC'elot. 170 | - An active mapping slot can be skipped by hitting the `SPACE`-key while hovering the mouse over OSC'elot. 171 | - Settings of a mapping slot are copied from the previous slot: If you set up the first mapping slot and map further mapping slots afterwards, these settings are copied over. Useful for settings like Encoder Sensitivity and Controller mode. 172 | 173 |
174 | 175 | --- 176 | ## Controller Modes 177 | 178 | This pretty much works the same as `MIDI-CAT`. By default the mode is set to `DIRECT`. 179 | Encoders are always `DIRECT`. Buttons and faders can be changed to other modes below. 180 | You will likely only need to change this if using a hardware OSC controller. 181 | 182 | - **`Direct`**: Every received OSC message is directly applied to the mapped parameter (*default*). 183 | 184 | - **`Pickup (snap)`**: OSC messages are ignored until the control reaches the current value of the parameter. After that the OSC control is "snaped" unto the parameter and will only unsnap if the parameter is changed from within Rack, e.g. manually by mouse or preset-loading. 185 | 186 | - **`Pickup (jump)`**: Same as snap-mode, but the control will loose the parameter when jumping to another value. This mode can be used if your OSC controller supports switching templates and you don't want your parameters to change when loading a different template. 187 | 188 | - **`Toggle`**: Every OSC message toggles the parameter between its minimum and maximum value (usually for buttons) 189 | 190 | - **`Toggle + Value`**: Every OSC message toggles the parameter between its minimum and the control's value. 191 | 192 |
193 | 194 | --- 195 | ## Expander 196 | 197 | The expander adds a Poly `trigger` and `CV` output along with 8 individual `trigger` and `CV` outputs for any configured controllers. The default CV range is `-5V to 5V`, but this can be changed in the right-click menu. 198 | 199 | 200 | ![Expander1](./Oscelot-expander.gif) 201 | ![Expander2](./Oscelot-exp.png) 202 | -------------------------------------------------------------------------------- /docs/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/The-Modular-Mind/oscelot/81c5b530bb39a9405714a974ac5d07241e303d18/docs/screenshot.png -------------------------------------------------------------------------------- /plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "slug": "OSCelot", 3 | "version": "2.0.0", 4 | "license": "GPL-3.0-only", 5 | "author": "TheModularMind", 6 | "name": "OSCelot", 7 | "brand": "TheModularMind", 8 | "authorEmail": "", 9 | "pluginUrl": "https://github.com/The-Modular-Mind/oscelot", 10 | "authorUrl": "https://github.com/The-Modular-Mind", 11 | "sourceUrl": "https://github.com/The-Modular-Mind/oscelot.git", 12 | "manualUrl": "https://github.com/The-Modular-Mind/oscelot/blob/master/docs/Oscelot.md", 13 | "changelogUrl": "https://github.com/The-Modular-Mind/oscelot/blob/master/CHANGELOG.md", 14 | "donateUrl": "https://paypal.me/themodularmind", 15 | "modules": [ 16 | { 17 | "slug": "OSCelot", 18 | "name": "OSCelot", 19 | "description": "OSC mapping module for parameters of any module", 20 | "tags": ["Utility"], 21 | "manualUrl": "https://github.com/The-Modular-Mind/oscelot/blob/master/docs/Oscelot.md" 22 | }, 23 | { 24 | "slug": "OSCelotExpander", 25 | "name": "OSCelotExpander", 26 | "description": "Expander for OSC'elot outputting a trigger and CV for each controller", 27 | "tags": ["Expander"], 28 | "manualUrl": "https://github.com/The-Modular-Mind/oscelot/blob/master/docs/Oscelot.md#expander" 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /presets/8FEB_OpenStageControl.json: -------------------------------------------------------------------------------- 1 | { 2 | "createdWith": "Open Stage Control", 3 | "version": "1.9.12", 4 | "type": "session", 5 | "content": { 6 | "type": "root", 7 | "id": "root", 8 | "visible": true, 9 | "interaction": true, 10 | "comments": "", 11 | "width": "auto", 12 | "height": "auto", 13 | "colorText": "auto", 14 | "colorWidget": "auto", 15 | "alphaFillOn": "auto", 16 | "padding": "auto", 17 | "html": "", 18 | "css": "", 19 | "colorBg": "auto", 20 | "layout": "default", 21 | "justify": "start", 22 | "gridTemplate": "", 23 | "contain": true, 24 | "scroll": true, 25 | "innerPadding": true, 26 | "verticalTabs": false, 27 | "variables": "@{parent.variables}", 28 | "traversing": false, 29 | "value": "", 30 | "default": "", 31 | "linkId": "", 32 | "address": "auto", 33 | "preArgs": "", 34 | "typeTags": "", 35 | "decimals": 2, 36 | "target": "", 37 | "ignoreDefaults": false, 38 | "bypass": false, 39 | "script": "", 40 | "widgets": [ 41 | { 42 | "type": "panel", 43 | "top": "auto", 44 | "left": "auto", 45 | "id": "panel_2", 46 | "visible": true, 47 | "interaction": true, 48 | "comments": "", 49 | "width": "100%", 50 | "height": "100%", 51 | "expand": "false", 52 | "colorText": "auto", 53 | "colorWidget": "auto", 54 | "colorStroke": "auto", 55 | "colorFill": "auto", 56 | "alphaStroke": "auto", 57 | "alphaFillOff": "auto", 58 | "alphaFillOn": "auto", 59 | "lineWidth": "auto", 60 | "padding": "auto", 61 | "html": "", 62 | "css": "", 63 | "colorBg": "auto", 64 | "layout": "default", 65 | "justify": "start", 66 | "gridTemplate": "", 67 | "contain": true, 68 | "scroll": true, 69 | "innerPadding": true, 70 | "verticalTabs": false, 71 | "variables": "@{parent.variables}", 72 | "traversing": false, 73 | "value": "", 74 | "default": "", 75 | "linkId": "", 76 | "address": "auto", 77 | "preArgs": "", 78 | "typeTags": "", 79 | "decimals": 2, 80 | "target": "", 81 | "ignoreDefaults": false, 82 | "bypass": false, 83 | "script": "", 84 | "widgets": [ 85 | { 86 | "type": "matrix", 87 | "top": "30%", 88 | "left": "0%", 89 | "id": "EncoderLabelScript", 90 | "visible": false, 91 | "interaction": true, 92 | "comments": "", 93 | "width": "auto", 94 | "height": "5%", 95 | "expand": "false", 96 | "colorText": "auto", 97 | "colorWidget": "rgba(23,110,197,1)", 98 | "colorStroke": "auto", 99 | "colorFill": "auto", 100 | "alphaStroke": "auto", 101 | "alphaFillOff": "auto", 102 | "alphaFillOn": "auto", 103 | "lineWidth": "auto", 104 | "padding": 0, 105 | "html": "", 106 | "css": "", 107 | "colorBg": "auto", 108 | "layout": "horizontal", 109 | "justify": "center", 110 | "gridTemplate": "", 111 | "contain": true, 112 | "scroll": false, 113 | "innerPadding": false, 114 | "variables": "@{parent.variables}", 115 | "traversing": false, 116 | "widgetType": "text", 117 | "quantity": 8, 118 | "props": "JS{{\nvar props = {}\nprops.wrap= \"soft\"\nprops.address = \"/encoder/info\"\nprops.preArgs = [$]\nprops.script = \"var label=value[0] + '\\\\n'+ value[1] + '\\\\n'+ value[2] + ' '+ value[3]; set('encoderLabels/#{$}', label);\"\nreturn props\n}}", 119 | "value": "", 120 | "default": "", 121 | "linkId": "", 122 | "address": "auto", 123 | "preArgs": "", 124 | "typeTags": "", 125 | "decimals": 2, 126 | "target": "", 127 | "ignoreDefaults": false, 128 | "bypass": false, 129 | "script": "", 130 | "widgets": [], 131 | "tabs": [] 132 | }, 133 | { 134 | "type": "matrix", 135 | "top": "30%", 136 | "left": "0%", 137 | "id": "ButtonLabelScript", 138 | "visible": false, 139 | "interaction": true, 140 | "comments": "", 141 | "width": "auto", 142 | "height": "5%", 143 | "expand": "false", 144 | "colorText": "auto", 145 | "colorWidget": "rgba(23,110,197,1)", 146 | "colorStroke": "auto", 147 | "colorFill": "auto", 148 | "alphaStroke": "auto", 149 | "alphaFillOff": "auto", 150 | "alphaFillOn": "auto", 151 | "lineWidth": "auto", 152 | "padding": 0, 153 | "html": "", 154 | "css": "", 155 | "colorBg": "auto", 156 | "layout": "grid", 157 | "justify": "center", 158 | "gridTemplate": 8, 159 | "contain": true, 160 | "scroll": false, 161 | "innerPadding": false, 162 | "variables": "@{parent.variables}", 163 | "traversing": false, 164 | "widgetType": "text", 165 | "quantity": 16, 166 | "props": "JS{{\nvar props = {}\nprops.wrap= \"soft\"\nprops.address = \"/button/info\"\nprops.preArgs = [$]\nprops.script = \"var label=value[0] + '\\\\n'+ value[1]; set('buttonLabels/#{$}', label);\"\nreturn props\n}}", 167 | "value": "", 168 | "default": "", 169 | "linkId": "", 170 | "address": "auto", 171 | "preArgs": "", 172 | "typeTags": "", 173 | "decimals": 2, 174 | "target": "", 175 | "ignoreDefaults": false, 176 | "bypass": false, 177 | "script": "", 178 | "widgets": [], 179 | "tabs": [] 180 | }, 181 | { 182 | "type": "matrix", 183 | "top": "30%", 184 | "left": "0%", 185 | "id": "FaderLabelScript", 186 | "visible": false, 187 | "interaction": true, 188 | "comments": "", 189 | "width": "auto", 190 | "height": "5%", 191 | "expand": "false", 192 | "colorText": "auto", 193 | "colorWidget": "rgba(23,110,197,1)", 194 | "colorStroke": "auto", 195 | "colorFill": "auto", 196 | "alphaStroke": "auto", 197 | "alphaFillOff": "auto", 198 | "alphaFillOn": "auto", 199 | "lineWidth": "auto", 200 | "padding": 0, 201 | "html": "", 202 | "css": "", 203 | "colorBg": "auto", 204 | "layout": "horizontal", 205 | "justify": "center", 206 | "gridTemplate": "", 207 | "contain": true, 208 | "scroll": false, 209 | "innerPadding": false, 210 | "variables": "@{parent.variables}", 211 | "traversing": false, 212 | "widgetType": "text", 213 | "quantity": 8, 214 | "props": "JS{{\nvar props = {}\nprops.wrap= \"soft\"\nprops.address = \"/fader/info\"\nprops.preArgs = [$]\nprops.script = \"var label=value[0] + '\\\\n'+ value[1] + '\\\\n'+ value[2] + ' '+ value[3]; set('faderLabels/#{$}', label);\"\nreturn props\n}}", 215 | "value": "", 216 | "default": "", 217 | "linkId": "", 218 | "address": "auto", 219 | "preArgs": "", 220 | "typeTags": "", 221 | "decimals": 2, 222 | "target": "", 223 | "ignoreDefaults": false, 224 | "bypass": false, 225 | "script": "", 226 | "widgets": [], 227 | "tabs": [] 228 | }, 229 | { 230 | "type": "matrix", 231 | "top": "60%", 232 | "left": "0%", 233 | "id": "buttons", 234 | "visible": true, 235 | "interaction": true, 236 | "comments": "", 237 | "width": "100%", 238 | "height": "20%", 239 | "expand": "false", 240 | "colorText": "auto", 241 | "colorWidget": "auto", 242 | "colorStroke": "auto", 243 | "colorFill": "auto", 244 | "alphaStroke": "auto", 245 | "alphaFillOff": "auto", 246 | "alphaFillOn": "auto", 247 | "lineWidth": "auto", 248 | "padding": "auto", 249 | "html": "", 250 | "css": "", 251 | "colorBg": "auto", 252 | "layout": "horizontal", 253 | "justify": "start", 254 | "gridTemplate": "", 255 | "contain": true, 256 | "scroll": false, 257 | "innerPadding": false, 258 | "variables": "@{parent.variables}", 259 | "traversing": false, 260 | "widgetType": "button", 261 | "quantity": 8, 262 | "props": "JS{{\nvar props = {}\nprops.on= 1\nprops.off= 0\nprops.mode = \"toggle\"\nprops.address = \"/button\"\nprops.preArgs = [$]\nprops.typeTags = \"if\"\nprops.label=\"\"\nreturn props\n}}", 263 | "value": "", 264 | "default": "", 265 | "linkId": "", 266 | "address": "/button", 267 | "preArgs": "", 268 | "typeTags": "", 269 | "decimals": 2, 270 | "target": "", 271 | "ignoreDefaults": false, 272 | "bypass": false, 273 | "script": "", 274 | "widgets": [], 275 | "tabs": [] 276 | }, 277 | { 278 | "type": "matrix", 279 | "top": "0%", 280 | "left": "0%", 281 | "id": "faders", 282 | "visible": true, 283 | "interaction": true, 284 | "comments": "", 285 | "width": "100%", 286 | "height": "60%", 287 | "expand": "false", 288 | "colorText": "auto", 289 | "colorWidget": "rgba(23,110,197,1)", 290 | "colorStroke": "auto", 291 | "colorFill": "auto", 292 | "alphaStroke": "auto", 293 | "alphaFillOff": "auto", 294 | "alphaFillOn": "auto", 295 | "lineWidth": "auto", 296 | "padding": "auto", 297 | "html": "", 298 | "css": "", 299 | "colorBg": "auto", 300 | "layout": "horizontal", 301 | "justify": "start", 302 | "gridTemplate": "", 303 | "contain": true, 304 | "scroll": false, 305 | "innerPadding": false, 306 | "variables": "@{parent.variables}", 307 | "traversing": false, 308 | "widgetType": "fader", 309 | "quantity": 8, 310 | "props": "JS{{\nvar props = {}\nprops.design= \"compact\"\nprops.gradient={\n \"0\": \"blue\",\n \"0.70\": \"purple\",\n \"0.90\": \"red\"\n}\nprops.knobSize= 25\nprops.address = \"/fader\"\nprops.preArgs = [$]\nprops.typeTags = \"if\"\nreturn props\n}}", 311 | "value": "", 312 | "default": "", 313 | "linkId": "", 314 | "address": "/button", 315 | "preArgs": "", 316 | "typeTags": "if", 317 | "decimals": 2, 318 | "target": "", 319 | "ignoreDefaults": false, 320 | "bypass": false, 321 | "script": "", 322 | "widgets": [], 323 | "tabs": [] 324 | }, 325 | { 326 | "type": "matrix", 327 | "top": "45%", 328 | "left": "0%", 329 | "id": "faderLabels", 330 | "visible": true, 331 | "interaction": false, 332 | "comments": "", 333 | "width": "100%", 334 | "height": "10%", 335 | "expand": "false", 336 | "colorText": "auto", 337 | "colorWidget": "rgba(23,110,197,0)", 338 | "colorStroke": "rgba(23,110,197,0)", 339 | "colorFill": "auto", 340 | "alphaStroke": "auto", 341 | "alphaFillOff": "auto", 342 | "alphaFillOn": "auto", 343 | "lineWidth": "auto", 344 | "padding": 0, 345 | "html": "", 346 | "css": "", 347 | "colorBg": "rgba(255,255,255,0)", 348 | "layout": "horizontal", 349 | "justify": "center", 350 | "gridTemplate": "", 351 | "contain": true, 352 | "scroll": false, 353 | "innerPadding": false, 354 | "variables": "@{parent.variables}", 355 | "traversing": false, 356 | "widgetType": "text", 357 | "quantity": 8, 358 | "props": "JS{{\nvar props = {}\nprops.wrap= \"soft\"\nreturn props\n}}", 359 | "value": "", 360 | "default": "", 361 | "linkId": "", 362 | "address": "auto", 363 | "preArgs": "", 364 | "typeTags": "", 365 | "decimals": 2, 366 | "target": "", 367 | "ignoreDefaults": false, 368 | "bypass": false, 369 | "script": "", 370 | "widgets": [], 371 | "tabs": [] 372 | }, 373 | { 374 | "type": "matrix", 375 | "top": "60%", 376 | "left": "0%", 377 | "id": "buttonLabels", 378 | "visible": true, 379 | "interaction": false, 380 | "comments": "", 381 | "width": "100%", 382 | "height": "20%", 383 | "expand": "false", 384 | "colorText": "auto", 385 | "colorWidget": "rgba(23,110,197,0)", 386 | "colorStroke": "rgba(23,110,197,0)", 387 | "colorFill": "auto", 388 | "alphaStroke": "auto", 389 | "alphaFillOff": "auto", 390 | "alphaFillOn": "auto", 391 | "lineWidth": "auto", 392 | "padding": 0, 393 | "html": "", 394 | "css": "", 395 | "colorBg": "rgba(255,255,255,0)", 396 | "layout": "horizontal", 397 | "justify": "center", 398 | "gridTemplate": "", 399 | "contain": true, 400 | "scroll": false, 401 | "innerPadding": false, 402 | "variables": "@{parent.variables}", 403 | "traversing": false, 404 | "widgetType": "text", 405 | "quantity": 8, 406 | "props": "JS{{\nvar props = {}\nprops.wrap= \"soft\"\nreturn props\n}}", 407 | "value": "", 408 | "default": "", 409 | "linkId": "", 410 | "address": "auto", 411 | "preArgs": "", 412 | "typeTags": "", 413 | "decimals": 2, 414 | "target": "", 415 | "ignoreDefaults": false, 416 | "bypass": false, 417 | "script": "", 418 | "widgets": [], 419 | "tabs": [] 420 | }, 421 | { 422 | "type": "matrix", 423 | "top": "80%", 424 | "left": "0%", 425 | "id": "encoders", 426 | "visible": true, 427 | "interaction": true, 428 | "comments": "", 429 | "width": "100%", 430 | "height": "20%", 431 | "expand": "false", 432 | "colorText": "auto", 433 | "colorWidget": "auto", 434 | "colorStroke": "auto", 435 | "colorFill": "auto", 436 | "alphaStroke": "auto", 437 | "alphaFillOff": "auto", 438 | "alphaFillOn": "auto", 439 | "lineWidth": "auto", 440 | "padding": "auto", 441 | "html": "", 442 | "css": "", 443 | "colorBg": "auto", 444 | "layout": "horizontal", 445 | "justify": "center", 446 | "gridTemplate": "", 447 | "contain": true, 448 | "scroll": false, 449 | "innerPadding": false, 450 | "variables": "@{parent.variables}", 451 | "traversing": false, 452 | "widgetType": "encoder", 453 | "quantity": 8, 454 | "props": "JS{{\nvar props = {}\nprops.address = \"/encoder\"\nprops.preArgs = [$]\nprops.typeTags = \"if\"\nreturn props\n}}", 455 | "value": "", 456 | "default": "", 457 | "linkId": "", 458 | "address": "/encoder", 459 | "preArgs": "", 460 | "typeTags": "if", 461 | "decimals": 2, 462 | "target": "", 463 | "ignoreDefaults": false, 464 | "bypass": false, 465 | "script": "", 466 | "widgets": [], 467 | "tabs": [] 468 | }, 469 | { 470 | "type": "matrix", 471 | "top": "80%", 472 | "left": "0%", 473 | "id": "encoderLabels", 474 | "visible": true, 475 | "interaction": false, 476 | "comments": "", 477 | "width": "100%", 478 | "height": "20%", 479 | "expand": "false", 480 | "colorText": "auto", 481 | "colorWidget": "rgba(23,110,197,0)", 482 | "colorStroke": "rgba(23,110,197,0)", 483 | "colorFill": "auto", 484 | "alphaStroke": "auto", 485 | "alphaFillOff": "auto", 486 | "alphaFillOn": "auto", 487 | "lineWidth": "auto", 488 | "padding": "auto", 489 | "html": "", 490 | "css": "", 491 | "colorBg": "rgba(255,255,255,0)", 492 | "layout": "horizontal", 493 | "justify": "center", 494 | "gridTemplate": "", 495 | "contain": true, 496 | "scroll": false, 497 | "innerPadding": false, 498 | "variables": "@{parent.variables}", 499 | "traversing": false, 500 | "widgetType": "text", 501 | "quantity": 8, 502 | "props": "JS{{\nvar props = {}\nprops.wrap= \"soft\"\nreturn props\n}}", 503 | "value": "", 504 | "default": "", 505 | "linkId": "", 506 | "address": "auto", 507 | "preArgs": "", 508 | "typeTags": "", 509 | "decimals": 2, 510 | "target": "", 511 | "ignoreDefaults": false, 512 | "bypass": false, 513 | "script": "", 514 | "widgets": [], 515 | "tabs": [] 516 | } 517 | ], 518 | "tabs": [] 519 | } 520 | ], 521 | "tabs": [] 522 | } 523 | } -------------------------------------------------------------------------------- /res/components/Port.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /res/components/Screw.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /res/components/SliderHandle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /res/components/SliderHorizontal.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /res/components/paw.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /res/components/paw0.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /res/components/paw1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /res/fonts/NovaMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/The-Modular-Mind/oscelot/81c5b530bb39a9405714a974ac5d07241e303d18/res/fonts/NovaMono-Regular.ttf -------------------------------------------------------------------------------- /res/fonts/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, wmk69 (wmk69@o2.pl), 2 | with Reserved Font Name NovaMono. 3 | 4 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 5 | This license is copied below, and is also available with a FAQ at: 6 | http://scripts.sil.org/OFL 7 | 8 | 9 | ----------------------------------------------------------- 10 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 11 | ----------------------------------------------------------- 12 | 13 | PREAMBLE 14 | The goals of the Open Font License (OFL) are to stimulate worldwide 15 | development of collaborative font projects, to support the font creation 16 | efforts of academic and linguistic communities, and to provide a free and 17 | open framework in which fonts may be shared and improved in partnership 18 | with others. 19 | 20 | The OFL allows the licensed fonts to be used, studied, modified and 21 | redistributed freely as long as they are not sold by themselves. The 22 | fonts, including any derivative works, can be bundled, embedded, 23 | redistributed and/or sold with any software provided that any reserved 24 | names are not used by derivative works. The fonts and derivatives, 25 | however, cannot be released under any other type of license. The 26 | requirement for fonts to remain under this license does not apply 27 | to any document created using the fonts or their derivatives. 28 | 29 | DEFINITIONS 30 | "Font Software" refers to the set of files released by the Copyright 31 | Holder(s) under this license and clearly marked as such. This may 32 | include source files, build scripts and documentation. 33 | 34 | "Reserved Font Name" refers to any names specified as such after the 35 | copyright statement(s). 36 | 37 | "Original Version" refers to the collection of Font Software components as 38 | distributed by the Copyright Holder(s). 39 | 40 | "Modified Version" refers to any derivative made by adding to, deleting, 41 | or substituting -- in part or in whole -- any of the components of the 42 | Original Version, by changing formats or by porting the Font Software to a 43 | new environment. 44 | 45 | "Author" refers to any designer, engineer, programmer, technical 46 | writer or other person who contributed to the Font Software. 47 | 48 | PERMISSION & CONDITIONS 49 | Permission is hereby granted, free of charge, to any person obtaining 50 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 51 | redistribute, and sell modified and unmodified copies of the Font 52 | Software, subject to the following conditions: 53 | 54 | 1) Neither the Font Software nor any of its individual components, 55 | in Original or Modified Versions, may be sold by itself. 56 | 57 | 2) Original or Modified Versions of the Font Software may be bundled, 58 | redistributed and/or sold with any software, provided that each copy 59 | contains the above copyright notice and this license. These can be 60 | included either as stand-alone text files, human-readable headers or 61 | in the appropriate machine-readable metadata fields within text or 62 | binary files as long as those fields can be easily viewed by the user. 63 | 64 | 3) No Modified Version of the Font Software may use the Reserved Font 65 | Name(s) unless explicit written permission is granted by the corresponding 66 | Copyright Holder. This restriction only applies to the primary font name as 67 | presented to the users. 68 | 69 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 70 | Software shall not be used to promote, endorse or advertise any 71 | Modified Version, except to acknowledge the contribution(s) of the 72 | Copyright Holder(s) and the Author(s) or with their explicit written 73 | permission. 74 | 75 | 5) The Font Software, modified or unmodified, in part or in whole, 76 | must be distributed entirely under this license, and must not be 77 | distributed under any other license. The requirement for fonts to 78 | remain under this license does not apply to any document created 79 | using the Font Software. 80 | 81 | TERMINATION 82 | This license becomes null and void if any of the above conditions are 83 | not met. 84 | 85 | DISCLAIMER 86 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 87 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 88 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 89 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 90 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 91 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 92 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 93 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 94 | OTHER DEALINGS IN THE FONT SOFTWARE. 95 | -------------------------------------------------------------------------------- /src/MapModuleBase.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "plugin.hpp" 3 | #include "settings.hpp" 4 | #include "components/ParamHandleIndicator.hpp" 5 | #include "components/OscelotParam.hpp" 6 | 7 | namespace TheModularMind { 8 | 9 | // Widgets 10 | 11 | template< int MAX_PARAMS, typename MODULE > 12 | struct MapModuleChoice : LedDisplayChoice { 13 | MODULE* module = NULL; 14 | bool processEvents = true; 15 | int id; 16 | 17 | std::chrono::time_point hscrollUpdate = std::chrono::system_clock::now(); 18 | int hscrollCharOffset = 0; 19 | 20 | MapModuleChoice() { 21 | box.size = mm2px(Vec(0, 7.5)); 22 | textOffset = Vec(6, 14.7); 23 | fontPath = asset::plugin(pluginInstance, "res/fonts/NovaMono-Regular.ttf"); 24 | color = nvgRGB(0xf0, 0xf0, 0xf0); 25 | } 26 | 27 | ~MapModuleChoice() { 28 | if (module && module->learningId == id) { 29 | glfwSetCursor(APP->window->win, NULL); 30 | } 31 | } 32 | 33 | void setModule(MODULE* module) { 34 | this->module = module; 35 | } 36 | 37 | void onButton(const event::Button& e) override { 38 | e.stopPropagating(); 39 | if (!module) return; 40 | if (module->locked) return; 41 | 42 | if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT) { 43 | e.consume(this); 44 | } 45 | 46 | if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) { 47 | e.consume(this); 48 | 49 | if (module->paramHandles[id].moduleId >= 0) { 50 | createContextMenu(); 51 | } 52 | else { 53 | module->clearMap(id); 54 | } 55 | } 56 | } 57 | 58 | void createContextMenu() { 59 | 60 | struct LabelMenuItem : MenuItem { 61 | MODULE* module; 62 | int id; 63 | std::string tempLabel; 64 | 65 | LabelMenuItem() { 66 | rightText = RIGHT_ARROW; 67 | } 68 | 69 | struct LabelField : ui::TextField { 70 | MODULE* module; 71 | int id; 72 | void onSelectKey(const event::SelectKey& e) override { 73 | if (e.action == GLFW_PRESS && e.key == GLFW_KEY_ENTER) { 74 | module->textLabels[id] = text; 75 | 76 | ui::MenuOverlay* overlay = getAncestorOfType(); 77 | overlay->requestDelete(); 78 | e.consume(this); 79 | } 80 | 81 | if (!e.getTarget()) { 82 | ui::TextField::onSelectKey(e); 83 | } 84 | } 85 | }; 86 | 87 | Menu* createChildMenu() override { 88 | Menu* menu = new Menu; 89 | 90 | LabelField* labelField = new LabelField; 91 | labelField->placeholder = "Label"; 92 | labelField->box.size.x = 220; 93 | labelField->module = module; 94 | labelField->id = id; 95 | labelField->text = module->textLabels[id]; 96 | if(labelField->text==""){ 97 | labelField->text=tempLabel; 98 | } 99 | menu->addChild(labelField); 100 | menu->addChild(createMenuItem("Reset", "", [=]() { module->textLabels[id] = ""; })); 101 | 102 | return menu; 103 | } 104 | }; // struct LabelMenuItem 105 | 106 | ui::Menu* menu = createMenu(); 107 | menu->addChild(createMenuLabel("Parameter \"" + getParamName() + "\"")); 108 | 109 | menu->addChild(construct( 110 | &MenuItem::text, "Custom label", &LabelMenuItem::module, module, &LabelMenuItem::id, id, 111 | &LabelMenuItem::tempLabel, getSlotPrefix() == ".... " ? getParamName() : getSlotPrefix() + getParamName())); 112 | 113 | menu->addChild(createMenuItem("Locate and indicate", "", [=]() { 114 | ParamHandle* paramHandle = &module->paramHandles[id]; 115 | ModuleWidget* mw = APP->scene->rack->getModule(paramHandle->moduleId); 116 | module->paramHandleIndicator[id].indicate(mw); 117 | })); 118 | 119 | menu->addChild(new MenuSeparator()); 120 | menu->addChild(createMenuItem("Unmap", "", [=]() { module->clearMap(id); })); 121 | appendContextMenu(menu); 122 | } 123 | 124 | virtual void appendContextMenu(Menu* menu) { } 125 | 126 | void onSelect(const event::Select& e) override { 127 | if (!module) return; 128 | if (module->locked) return; 129 | 130 | ScrollWidget *scroll = getAncestorOfType(); 131 | scroll->scrollTo(box); 132 | 133 | // Reset touchedParam, unstable API 134 | APP->scene->rack->touchedParam = NULL; 135 | module->enableLearn(id); 136 | 137 | GLFWcursor* cursor = glfwCreateStandardCursor(GLFW_CROSSHAIR_CURSOR); 138 | glfwSetCursor(APP->window->win, cursor); 139 | } 140 | 141 | void onDeselect(const event::Deselect& e) override { 142 | if (!module) return; 143 | if (!processEvents) return; 144 | 145 | // Check if a ParamWidget was touched, unstable API 146 | ParamWidget *touchedParam = APP->scene->rack->touchedParam; 147 | if (touchedParam && touchedParam->getParamQuantity()->module != module) { 148 | APP->scene->rack->touchedParam = NULL; 149 | int64_t moduleId = touchedParam->getParamQuantity()->module->id; 150 | int paramId = touchedParam->getParamQuantity()->paramId; 151 | module->learnParam(id, moduleId, paramId); 152 | hscrollCharOffset = 0; 153 | } 154 | else { 155 | module->disableLearn(id); 156 | } 157 | glfwSetCursor(APP->window->win, NULL); 158 | } 159 | 160 | void step() override { 161 | if (!module) 162 | return; 163 | 164 | if (module->learningId == id) { 165 | bgColor = color; 166 | bgColor.a = 0.15; 167 | if (APP->event->getSelectedWidget() != this) 168 | APP->event->setSelectedWidget(this); 169 | } 170 | else { 171 | bgColor = nvgRGBA(0, 0, 0, 0); 172 | if (APP->event->getSelectedWidget() == this) 173 | APP->event->setSelectedWidget(NULL); 174 | } 175 | 176 | // Set text 177 | if (module->paramHandles[id].moduleId >= 0 && module->learningId != id) { 178 | std::string prefix = ""; 179 | std::string label = getSlotLabel(); 180 | if (label == "") { 181 | prefix = getSlotPrefix(); 182 | label = getParamName(); 183 | if (label == "") { 184 | module->clearMap(id); 185 | return; 186 | } 187 | } 188 | 189 | size_t hscrollMaxLength = ceil(box.size.x / 6.2f); 190 | if (module->textScrolling && label.length() + prefix.length() > hscrollMaxLength) { 191 | // Scroll the parameter-name horizontically 192 | text = prefix + label.substr(hscrollCharOffset > (int)label.length() ? 0 : hscrollCharOffset); 193 | auto now = std::chrono::system_clock::now(); 194 | if (now - hscrollUpdate > std::chrono::milliseconds{100}) { 195 | hscrollCharOffset = (hscrollCharOffset + 1) % (label.length() + hscrollMaxLength); 196 | hscrollUpdate = now; 197 | } 198 | } 199 | else { 200 | text = prefix + label; 201 | } 202 | } 203 | else { 204 | if (module->learningId == id) { 205 | text = getSlotPrefix() + "Mapping..."; 206 | } else { 207 | text = getSlotPrefix() + "Unmapped"; 208 | } 209 | } 210 | 211 | // Set text color 212 | if (module->paramHandles[id].moduleId >= 0 || module->learningId == id) { 213 | color.a = 0.9; 214 | } 215 | else { 216 | color.a = 0.5; 217 | } 218 | } 219 | 220 | virtual std::string getSlotLabel() { 221 | return ""; 222 | } 223 | 224 | virtual std::string getSlotPrefix() { 225 | return MAX_PARAMS > 1 ? string::f("%02d ", id + 1) : ""; 226 | } 227 | 228 | ParamQuantity* getParamQuantity() { 229 | if (!module) 230 | return NULL; 231 | if (id >= module->mapLen) 232 | return NULL; 233 | ParamHandle* paramHandle = &module->paramHandles[id]; 234 | if (paramHandle->moduleId < 0) 235 | return NULL; 236 | ModuleWidget *mw = APP->scene->rack->getModule(paramHandle->moduleId); 237 | if (!mw) 238 | return NULL; 239 | // Get the Module from the ModuleWidget instead of the ParamHandle. 240 | // I think this is more elegant since this method is called in the app world instead of the engine world. 241 | Module* m = mw->module; 242 | if (!m) 243 | return NULL; 244 | int paramId = paramHandle->paramId; 245 | if (paramId >= (int) m->params.size()) 246 | return NULL; 247 | ParamQuantity* paramQuantity = m->paramQuantities[paramId]; 248 | return paramQuantity; 249 | } 250 | 251 | std::string getParamName() { 252 | if (!module) 253 | return ""; 254 | if (id >= module->mapLen) 255 | return ""; 256 | ParamHandle* paramHandle = &module->paramHandles[id]; 257 | if (paramHandle->moduleId < 0) 258 | return ""; 259 | ModuleWidget *mw = APP->scene->rack->getModule(paramHandle->moduleId); 260 | if (!mw) 261 | return ""; 262 | // Get the Module from the ModuleWidget instead of the ParamHandle. 263 | // I think this is more elegant since this method is called in the app world instead of the engine world. 264 | Module* m = mw->module; 265 | if (!m) 266 | return ""; 267 | int paramId = paramHandle->paramId; 268 | if (paramId >= (int) m->params.size()) 269 | return ""; 270 | ParamQuantity* paramQuantity = m->paramQuantities[paramId]; 271 | std::string s; 272 | s += mw->model->name; 273 | s += " > "; 274 | s += paramQuantity->name; 275 | return s; 276 | } 277 | 278 | void drawLayer(const DrawArgs& args, int layer) override { 279 | if (layer == 1) { 280 | if (bgColor.a > 0.0) { 281 | nvgScissor(args.vg, RECT_ARGS(args.clipBox)); 282 | nvgBeginPath(args.vg); 283 | nvgRect(args.vg, 0, 0, box.size.x, box.size.y); 284 | nvgFillColor(args.vg, bgColor); 285 | nvgFill(args.vg); 286 | nvgResetScissor(args.vg); 287 | } 288 | 289 | std::shared_ptr font = APP->window->loadFont(fontPath); 290 | 291 | if (font && font->handle >= 0) { 292 | Rect r = Rect(textOffset.x, 0.f, box.size.x - textOffset.x * 2, box.size.y).intersect(args.clipBox); 293 | nvgScissor(args.vg, RECT_ARGS(r)); 294 | nvgFillColor(args.vg, color); 295 | nvgFontFaceId(args.vg, font->handle); 296 | nvgTextLetterSpacing(args.vg, 0.0); 297 | nvgFontSize(args.vg, 14); 298 | nvgText(args.vg, textOffset.x, textOffset.y, text.c_str(), NULL); 299 | nvgResetScissor(args.vg); 300 | } 301 | } 302 | } 303 | }; 304 | 305 | struct OscelotScrollWidget : ScrollWidget { 306 | void draw(const DrawArgs& args) override { 307 | NVGcolor color = color::BLACK; 308 | nvgScissor(args.vg, RECT_ARGS(args.clipBox)); 309 | Widget::draw(args); 310 | nvgResetScissor(args.vg); 311 | 312 | if(verticalScrollbar->visible){ 313 | color.a = 0.5; 314 | nvgBeginPath(args.vg); 315 | nvgRoundedRect(args.vg, verticalScrollbar->box.pos.x, verticalScrollbar->box.pos.y, verticalScrollbar->box.size.x, verticalScrollbar->box.size.y, 3.0); 316 | nvgFillColor(args.vg, color); 317 | nvgFill(args.vg); 318 | 319 | color.a = 0.4; 320 | nvgBeginPath(args.vg); 321 | nvgRoundedRect(args.vg, verticalScrollbar->box.pos.x+1, verticalScrollbar->box.pos.y+1, verticalScrollbar->box.size.x - 2, verticalScrollbar->box.size.y - 2, 3.0); 322 | nvgFillColor(args.vg, color); 323 | nvgFill(args.vg); 324 | } 325 | } 326 | }; 327 | 328 | template< int MAX_PARAMS, typename MODULE, typename CHOICE = MapModuleChoice > 329 | struct MapModuleDisplay : LedDisplay { 330 | MODULE* module; 331 | ScrollWidget* scroll; 332 | CHOICE* choices[MAX_PARAMS]; 333 | 334 | ~MapModuleDisplay() { 335 | for (int id = 0; id < MAX_PARAMS; id++) { 336 | choices[id]->processEvents = false; 337 | } 338 | } 339 | 340 | void setModule(MODULE* module) { 341 | this->module = module; 342 | 343 | scroll = new OscelotScrollWidget(); 344 | scroll->box.size.x = box.size.x; 345 | scroll->box.size.y = box.size.y - scroll->box.pos.y; 346 | scroll->verticalScrollbar->box.size.x = 8.0f; 347 | 348 | addChild(scroll); 349 | 350 | Vec pos; 351 | for (int id = 0; id < MAX_PARAMS; id++) { 352 | CHOICE* choice = createWidget(pos); 353 | choice->box.size.x = box.size.x; 354 | choice->id = id; 355 | choice->setModule(module); 356 | scroll->container->addChild(choice); 357 | choices[id] = choice; 358 | 359 | pos = choice->box.getBottomLeft(); 360 | } 361 | } 362 | 363 | void draw(const DrawArgs& args) override { 364 | NVGcolor bgColor = color::BLACK; 365 | bgColor.a=0.0; 366 | // LedDisplay::draw(args); 367 | 368 | nvgBeginPath(args.vg); 369 | nvgRoundedRect(args.vg, 0, 0, box.size.x, box.size.y, 5.0); 370 | nvgFillColor(args.vg, bgColor); 371 | nvgFill(args.vg); 372 | 373 | nvgScissor(args.vg, RECT_ARGS(args.clipBox)); 374 | Widget::draw(args); 375 | nvgResetScissor(args.vg); 376 | 377 | if (module && module->locked) { 378 | NVGcolor fColor = color::WHITE; 379 | fColor.a=0.10; 380 | nvgBeginPath(args.vg); 381 | nvgRoundedRect(args.vg, -2, -5, box.size.x + 8, box.size.y + 7, 25.0); 382 | nvgFillColor(args.vg, fColor); 383 | nvgFill(args.vg); 384 | } 385 | } 386 | 387 | void onHoverScroll(const event::HoverScroll& e) override { 388 | if (module && module->locked) { 389 | e.stopPropagating(); 390 | } 391 | LedDisplay::onHoverScroll(e); 392 | } 393 | }; 394 | 395 | 396 | } // namespace TheModularMind -------------------------------------------------------------------------------- /src/OscController.cpp: -------------------------------------------------------------------------------- 1 | #include "osc/OscController.hpp" 2 | #include 3 | 4 | namespace TheModularMind { 5 | 6 | class OscFader : public OscController { 7 | public: 8 | OscFader(std::string address, int controllerId, CONTROLLERMODE controllerMode, float value, uint32_t ts) { 9 | this->setTypeString("FDR"); 10 | this->setAddress(address); 11 | this->setControllerId(controllerId); 12 | this->setControllerMode(controllerMode); 13 | OscController::setCurrentValue(value, ts); 14 | } 15 | 16 | virtual bool setCurrentValue(float value, uint32_t ts) override { 17 | if (ts == 0 || ts > this->getTs()) { 18 | return OscController::setCurrentValue(value, ts); 19 | } 20 | return false; 21 | } 22 | }; 23 | 24 | class OscEncoder : public OscController { 25 | public: 26 | OscEncoder(std::string address, int controllerId, float value, uint32_t ts, int sensitivity = ENCODER_DEFAULT_SENSITIVITY) { 27 | this->setTypeString("ENC"); 28 | this->setAddress(address); 29 | this->setControllerId(controllerId); 30 | this->setControllerMode(CONTROLLERMODE::DIRECT); 31 | this->setSensitivity(sensitivity); 32 | this->setCurrentValue(value, ts); 33 | } 34 | 35 | virtual bool setCurrentValue(float value, uint32_t ts) override { 36 | if (ts == 0) { 37 | OscController::setCurrentValue(value, ts); 38 | } else if (ts > this->getTs()) { 39 | float newValue = this->getCurrentValue() + (value / float(sensitivity)); 40 | OscController::setCurrentValue(rack::math::clamp(newValue, 0.f, 1.f), ts); 41 | } 42 | return this->getCurrentValue() >= 0.f; 43 | } 44 | 45 | void setSensitivity(int sensitivity) override { this->sensitivity = sensitivity; } 46 | int getSensitivity() override { return this->sensitivity; } 47 | 48 | private: 49 | int sensitivity = ENCODER_DEFAULT_SENSITIVITY; 50 | }; 51 | 52 | class OscButton : public OscController { 53 | public: 54 | OscButton(std::string address, int controllerId, CONTROLLERMODE controllerMode, float value, uint32_t ts) { 55 | this->setTypeString("BTN"); 56 | this->setAddress(address); 57 | this->setControllerId(controllerId); 58 | this->setControllerMode(controllerMode); 59 | OscController::setCurrentValue(value, ts); 60 | } 61 | 62 | virtual bool setCurrentValue(float value, uint32_t ts) override { 63 | if (ts == 0) { 64 | OscController::setCurrentValue(value, ts); 65 | } else if (ts > this->getTs()) { 66 | OscController::setCurrentValue(rack::math::clamp(value, 0.f, 1.0f), ts); 67 | } 68 | return this->getCurrentValue() >= 0.f; 69 | } 70 | }; 71 | 72 | bool endsWith(std::string const &fullString, std::string const &ending) { 73 | if (fullString.length() >= ending.length()) { 74 | return (0 == fullString.compare(fullString.length() - ending.length(), ending.length(), ending)); 75 | } else { 76 | return false; 77 | } 78 | } 79 | 80 | OscController *OscController::Create(std::string address, int controllerId, CONTROLLERMODE controllerMode, float value, uint32_t ts) { 81 | if (endsWith(address, "/fader")) { 82 | return new OscFader(address, controllerId, controllerMode, value, ts); 83 | } else if (endsWith(address, "/encoder")) { 84 | return new OscEncoder(address, controllerId, value, ts); 85 | } else if (endsWith(address, "/button")) { 86 | return new OscButton(address, controllerId, controllerMode, value, ts); 87 | } else 88 | INFO("Not Implemented for address: %s", address.c_str()); 89 | return nullptr; 90 | }; 91 | 92 | } // namespace TheModularMind -------------------------------------------------------------------------------- /src/Oscelot.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "plugin.hpp" 3 | #include "osc/OscSender.hpp" 4 | #include "osc/OscReceiver.hpp" 5 | #include "components/LedTextField.hpp" 6 | #include "components/MeowMory.hpp" 7 | #include "osc/OscController.hpp" 8 | 9 | namespace TheModularMind { 10 | namespace Oscelot { 11 | 12 | static const int MAX_PARAMS = 320; 13 | static const std::string RXPORT_DEFAULT = "8881"; 14 | static const std::string TXPORT_DEFAULT = "8880"; 15 | 16 | } // namespace Oscelot 17 | } // namespace TheModularMind -------------------------------------------------------------------------------- /src/OscelotExpander.cpp: -------------------------------------------------------------------------------- 1 | #include "OscelotExpander.hpp" 2 | 3 | namespace TheModularMind { 4 | namespace Oscelot { 5 | 6 | struct OscelotExpander : Module { 7 | enum ParamIds { NUM_PARAMS }; 8 | enum InputIds { NUM_INPUTS }; 9 | enum OutputIds { ENUMS(TRIG_OUTPUT, 8), ENUMS(CV_OUTPUT, 8), ENUMS(POLY_OUTPUT, 2), NUM_OUTPUTS }; 10 | enum LightIds { NUM_LIGHTS }; 11 | 12 | int panelTheme = rand() % 3; 13 | int expanderId; 14 | int startVoltageIndex = 1; 15 | int endVoltageIndex = 7; 16 | float controlVoltages[9] = { -10.0f, -5.0f, -3.0f, -1.0f , 0.0f, 1.0f, 3.0f, 5.0f, 10.0f }; 17 | dsp::ClockDivider processDivider; 18 | dsp::PulseGenerator pulseGenerator[8]; 19 | simd::float_4 last[2]; 20 | std::string labels[8]; 21 | 22 | OscelotExpander() { 23 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); 24 | onReset(); 25 | } 26 | 27 | void onReset() override { 28 | processDivider.setDivision(64); 29 | processDivider.reset(); 30 | 31 | for (int i = 0; i < 8; i++) { 32 | labels[i] = ""; 33 | last[i / 4][i % 4] = 0.0f; 34 | pulseGenerator[i].reset(); 35 | outputs[CV_OUTPUT + i].clearVoltages(); 36 | outputs[POLY_OUTPUT_LAST].clearVoltages(); 37 | } 38 | expanderId = 0; 39 | rightExpander.producerMessage = NULL; 40 | rightExpander.messageFlipRequested = false; 41 | } 42 | 43 | void process(const ProcessArgs& args) override { 44 | if (processDivider.process()) { 45 | Module* expanderMother = leftExpander.module; 46 | OscelotExpanderBase* module; 47 | 48 | if (!expanderMother || (expanderMother->model != modelOSCelot && expanderMother->model != modelOscelotExpander) || !expanderMother->rightExpander.consumerMessage) { 49 | onReset(); 50 | return; 51 | } 52 | 53 | ExpanderPayload* expPayload = reinterpret_cast(expanderMother->rightExpander.consumerMessage); 54 | module = reinterpret_cast(expPayload->base); 55 | expanderId = expPayload->expanderId; 56 | 57 | if (expanderId + 8 > MAX_PARAMS) return; 58 | 59 | float* values = module->expGetValues(); 60 | std::string* controllerLabels = module->expGetLabels(); 61 | outputs[POLY_OUTPUT].setChannels(8); 62 | outputs[POLY_OUTPUT_LAST].setChannels(8); 63 | 64 | for (int i = 0; i < 8; i++) { 65 | float v = values[i + expanderId]; 66 | if (v < 0.0f) return; 67 | labels[i] = controllerLabels[i + expanderId]; 68 | 69 | bool trig = simd::ifelse(last[i / 4][i % 4] != v, 1, 0); 70 | 71 | if (trig) { 72 | pulseGenerator[i].trigger(1e-3); 73 | last[i / 4][i % 4] = v; 74 | } 75 | 76 | simd::float_4 trigVoltage = pulseGenerator[i].process(args.sampleTime) ? 10.f : 0.f; 77 | simd::float_4 cvVoltage = simd::rescale(v, 0.0f, 1.0f, controlVoltages[startVoltageIndex], controlVoltages[endVoltageIndex]); 78 | 79 | outputs[TRIG_OUTPUT + i].setVoltage(trigVoltage[0]); 80 | outputs[POLY_OUTPUT].setVoltage(trigVoltage[0], i); 81 | outputs[CV_OUTPUT + i].setVoltage(cvVoltage[0]); 82 | outputs[POLY_OUTPUT_LAST].setVoltage(cvVoltage[0], i); 83 | } 84 | 85 | rightExpander.producerMessage = new ExpanderPayload(module, expanderId + 8); 86 | rightExpander.messageFlipRequested = true; 87 | } 88 | } 89 | 90 | json_t* dataToJson() override { 91 | json_t* rootJ = json_object(); 92 | json_object_set_new(rootJ, "panelTheme", json_integer(panelTheme)); 93 | json_object_set_new(rootJ, "startVoltageIndex", json_real(startVoltageIndex)); 94 | json_object_set_new(rootJ, "endVoltageIndex", json_real(endVoltageIndex)); 95 | return rootJ; 96 | } 97 | 98 | void dataFromJson(json_t* rootJ) override { 99 | panelTheme = json_integer_value(json_object_get(rootJ, "panelTheme")); 100 | startVoltageIndex = json_real_value(json_object_get(rootJ, "startVoltageIndex")); 101 | endVoltageIndex = json_real_value(json_object_get(rootJ, "endVoltageIndex")); 102 | } 103 | }; 104 | 105 | struct OscLabelWidget : widget::OpaqueWidget { 106 | OscelotExpander* module; 107 | OscelotTextLabel* labelWidgets[8]; 108 | OscelotTextLabel* idLabel; 109 | NVGcolor color = nvgRGB(0xDA, 0xa5, 0x20); 110 | NVGcolor white = nvgRGB(0xfe, 0xff, 0xe0); 111 | Vec lblPos = box.pos; 112 | 113 | void step() override { 114 | if (!module) return; 115 | 116 | for (int i = 0; i < 8; i++) { 117 | if (labelWidgets[i]->text != module->labels[i]) labelWidgets[i]->text = module->labels[i]; 118 | } 119 | } 120 | 121 | void setLabels() { 122 | clearChildren(); 123 | 124 | OscelotTextLabel* l = createWidget(lblPos); 125 | l->box.size = mm2px(Vec(25.4, 1)); 126 | l->text = "POLY"; 127 | addChild(l); 128 | idLabel = l; 129 | lblPos = lblPos.plus(Vec(0.0f, 16.0f)); 130 | 131 | for (int i = 0; i < 8; i++) { 132 | lblPos = lblPos.plus(Vec(0.0f, 36.0f)); 133 | OscelotTextLabel* l = createWidget(lblPos); 134 | l->box.size = mm2px(Vec(25.4, 1)); 135 | l->text = module->labels[i]; 136 | addChild(l); 137 | labelWidgets[i] = l; 138 | } 139 | } 140 | }; 141 | 142 | struct OscelotExpanderWidget : ThemedModuleWidget { 143 | OscelotExpanderWidget(OscelotExpander* module) : ThemedModuleWidget(module, "OscelotExpander", "Oscelot.md#expander") { 144 | setModule(module); 145 | 146 | addChild(createWidget(Vec(2 * RACK_GRID_WIDTH, 0))); 147 | addChild(createWidget(Vec(box.size.x - 3 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); 148 | 149 | OscLabelWidget* oscLabelWidget = createWidget(mm2px(Vec(0, 7))); 150 | oscLabelWidget->module = module; 151 | if (module) { 152 | oscLabelWidget->setLabels(); 153 | } 154 | addChild(oscLabelWidget); 155 | 156 | Vec gatePos = mm2px(Vec(7, 14)); 157 | Vec cvPos = mm2px(Vec(18.4, 14)); 158 | 159 | addOutput(createOutputCentered(gatePos, module, OscelotExpander::POLY_OUTPUT)); 160 | addOutput(createOutputCentered(cvPos, module, OscelotExpander::POLY_OUTPUT_LAST)); 161 | gatePos = gatePos.plus(Vec(0.0f, 16.0f)); 162 | cvPos = cvPos.plus(Vec(0.0f, 16.0f)); 163 | 164 | for (int i = 0; i < 8; i++) { 165 | gatePos = gatePos.plus(Vec(0.0f, 36.0f)); 166 | cvPos = cvPos.plus(Vec(0.0f, 36.0f)); 167 | addOutput(createOutputCentered(gatePos, module, OscelotExpander::TRIG_OUTPUT + i)); 168 | addOutput(createOutputCentered(cvPos, module, OscelotExpander::CV_OUTPUT + i)); 169 | } 170 | } 171 | void appendContextMenu(Menu* menu) override { 172 | ThemedModuleWidget::appendContextMenu(menu); 173 | assert(module); 174 | 175 | menu->addChild(new MenuSeparator()); 176 | menu->addChild(createMenuLabel(string::f("CV Range: %.0fV to %.0fV", module->controlVoltages[ module->startVoltageIndex], module->controlVoltages[ module->endVoltageIndex]))); 177 | 178 | menu->addChild(createSubmenuItem("Configure CV", "", [=](Menu* menu) { 179 | menu->addChild(createSubmenuItem(string::f("Voltage Range (%.0fV)", abs(module->controlVoltages[module->startVoltageIndex] - module->controlVoltages[module->endVoltageIndex])), "", [=](Menu* menu) { 180 | menu->addChild(createCheckMenuItem( 181 | "-1V to 1V", "", [=]() { return module->startVoltageIndex == 3 && module->endVoltageIndex == 5; }, 182 | [=]() { 183 | module->startVoltageIndex = 3; 184 | module->endVoltageIndex = 5; 185 | })); 186 | menu->addChild(createCheckMenuItem( 187 | "-3V to 3V", "", [=]() { return module->startVoltageIndex == 2 && module->endVoltageIndex == 6; }, 188 | [=]() { 189 | module->startVoltageIndex = 2; 190 | module->endVoltageIndex = 6; 191 | })); 192 | menu->addChild(createCheckMenuItem( 193 | "-5V to 5V", "", [=]() { return module->startVoltageIndex == 1 && module->endVoltageIndex == 7; }, 194 | [=]() { 195 | module->startVoltageIndex = 1; 196 | module->endVoltageIndex = 7; 197 | })); 198 | menu->addChild(createCheckMenuItem( 199 | "-10V to 10V", "", [=]() { return module->startVoltageIndex == 0 && module->endVoltageIndex == 8; }, 200 | [=]() { 201 | module->startVoltageIndex = 0; 202 | module->endVoltageIndex = 8; 203 | })); 204 | menu->addChild(createCheckMenuItem( 205 | "0V to 1V", "", [=]() { return module->startVoltageIndex == 4 && module->endVoltageIndex == 5; }, 206 | [=]() { 207 | module->startVoltageIndex = 4; 208 | module->endVoltageIndex = 5; 209 | })); 210 | menu->addChild(createCheckMenuItem( 211 | "0V to 3V", "", [=]() { return module->startVoltageIndex == 4 && module->endVoltageIndex == 6; }, 212 | [=]() { 213 | module->startVoltageIndex = 4; 214 | module->endVoltageIndex = 6; 215 | })); 216 | menu->addChild(createCheckMenuItem( 217 | "0V to 5V", "", [=]() { return module->startVoltageIndex == 4 && module->endVoltageIndex == 7; }, 218 | [=]() { 219 | module->startVoltageIndex = 4; 220 | module->endVoltageIndex = 7; 221 | })); 222 | menu->addChild(createCheckMenuItem( 223 | "0V to 10V", "", [=]() { return module->startVoltageIndex == 4 && module->endVoltageIndex == 8; }, 224 | [=]() { 225 | module->startVoltageIndex = 4; 226 | module->endVoltageIndex = 8; 227 | })); 228 | })); 229 | menu->addChild(createIndexPtrSubmenuItem("Start Voltage", { "-10 V", "-5 V", "-3 V", "-1 V" , "0 V", "1 V", "3 V", "5 V", "10 V" }, &module->startVoltageIndex)); 230 | menu->addChild(createIndexPtrSubmenuItem("End Voltage", { "-10 V", "-5 V", "-3 V", "-1 V" , "0 V", "1 V", "3 V", "5 V", "10 V" }, &module->endVoltageIndex)); 231 | })); 232 | } 233 | }; 234 | } // namespace Oscelot 235 | } // namespace TheModularMind 236 | 237 | Model* modelOscelotExpander = createModel("OSCelotExpander"); -------------------------------------------------------------------------------- /src/OscelotExpander.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "plugin.hpp" 3 | #include "components/LedTextField.hpp" 4 | #include "Oscelot.hpp" 5 | 6 | namespace TheModularMind { 7 | namespace Oscelot { 8 | 9 | struct OscelotExpanderBase { 10 | virtual float* expGetValues() { return NULL; } 11 | virtual std::string* expGetLabels() { return NULL; } 12 | }; 13 | 14 | struct ExpanderPayload { 15 | OscelotExpanderBase* base; 16 | int expanderId; 17 | 18 | ExpanderPayload(OscelotExpanderBase* base, int expanderId) { 19 | this->base = base; 20 | this->expanderId = expanderId; 21 | } 22 | }; 23 | 24 | } // namespace Oscelot 25 | } // namespace TheModularMind -------------------------------------------------------------------------------- /src/components.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "plugin.hpp" 3 | #include "components/PawButtons.hpp" 4 | #include "ui/ThemedModuleWidget.hpp" -------------------------------------------------------------------------------- /src/components/LedTextField.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace TheModularMind { 4 | 5 | struct OscelotTextField : LedDisplayTextField { 6 | float textSize = 14.f; 7 | const static unsigned int defaultMaxTextLength = 5; 8 | unsigned int maxTextLength; 9 | NVGcolor bgColor; 10 | bool isFocused = false; 11 | bool doubleClick = false; 12 | 13 | OscelotTextField() { 14 | maxTextLength = defaultMaxTextLength; 15 | textOffset = math::Vec(-0.4f, -2.1f); 16 | color = nvgRGB(0xfe, 0xff, 0xe0); 17 | bgColor = color::BLACK; 18 | bgColor.a = 0.3; 19 | fontPath = asset::plugin(pluginInstance, "res/fonts/NovaMono-Regular.ttf"); 20 | } 21 | 22 | void draw(const DrawArgs& args) override { 23 | // Background 24 | if (bgColor.a > 0.0) { 25 | nvgBeginPath(args.vg); 26 | nvgRoundedRect(args.vg, 0, 0, box.size.x, box.size.y, 5.0); 27 | nvgFillColor(args.vg, bgColor); 28 | nvgFill(args.vg); 29 | } 30 | 31 | Widget::draw(args); 32 | } 33 | 34 | void drawLayer(const DrawArgs& args, int layer) override { 35 | if (layer == 1) { 36 | nvgScissor(args.vg, RECT_ARGS(args.clipBox)); 37 | 38 | std::shared_ptr font = APP->window->loadFont(fontPath); 39 | // Text 40 | if (font->handle >= 0) { 41 | bndSetFont(font->handle); 42 | 43 | NVGcolor highlightColor = color; 44 | highlightColor.a = 0.5; 45 | color.a = 0.9; 46 | int begin = std::min(cursor, selection); 47 | int end = (this == APP->event->selectedWidget) ? std::max(cursor, selection) : -1; 48 | bndIconLabelCaret(args.vg, textOffset.x, textOffset.y, box.size.x - 2 * textOffset.x, box.size.y - 2 * textOffset.y, -1, color, textSize, text.c_str(), 49 | highlightColor, begin, end); 50 | 51 | bndSetFont(APP->window->uiFont->handle); 52 | } 53 | nvgResetScissor(args.vg); 54 | } 55 | } 56 | 57 | void onSelect(const event::Select& e) override { 58 | isFocused = true; 59 | e.consume(this); 60 | } 61 | 62 | void onDeselect(const event::Deselect& e) override { 63 | isFocused = false; 64 | LedDisplayTextField::setText(TextField::text); 65 | e.consume(NULL); 66 | } 67 | 68 | void onAction(const event::Action& e) override { 69 | // this gets fired when the user types 'enter' 70 | event::Deselect eDeselect; 71 | onDeselect(eDeselect); 72 | APP->event->selectedWidget = NULL; 73 | e.consume(NULL); 74 | } 75 | 76 | void onDoubleClick(const event::DoubleClick& e) override { doubleClick = true; } 77 | 78 | void onButton(const event::Button& e) override { 79 | if (e.button == GLFW_MOUSE_BUTTON_LEFT && e.action == GLFW_RELEASE) { 80 | if (doubleClick) { 81 | doubleClick = false; 82 | selectAll(); 83 | } 84 | } 85 | LedDisplayTextField::onButton(e); 86 | } 87 | 88 | void onSelectText(const event::SelectText& e) override { 89 | if (TextField::text.size() < maxTextLength || cursor != selection) { 90 | LedDisplayTextField::onSelectText(e); 91 | } else { 92 | e.consume(NULL); 93 | } 94 | } 95 | }; 96 | 97 | struct OscelotTextLabel : ui::Label { 98 | float textSize = 8.f; 99 | bool drawBackground = true; 100 | NVGcolor color; 101 | NVGcolor bgColor; 102 | 103 | OscelotTextLabel() { 104 | color = nvgRGB(0xfe, 0xff, 0xe0); 105 | bgColor = color::WHITE; 106 | color.a = 0.9; 107 | bgColor.a = 0.05; 108 | } 109 | 110 | void drawLabel(const DrawArgs& args, float x0, float y0, float w, const char* label, int size) { 111 | float constexpr padMargin = 3; 112 | nvgBeginPath(args.vg); 113 | 114 | nvgFontSize(args.vg, size); 115 | nvgTextAlign(args.vg, NVG_ALIGN_TOP | NVG_ALIGN_CENTER); 116 | nvgFillColor(args.vg, color); 117 | nvgText(args.vg, x0 + w / 2, y0, label, NULL); 118 | 119 | if (drawBackground) { 120 | float bounds[4]; 121 | nvgTextBounds(args.vg, x0 + w / 2, y0, label, NULL, bounds); 122 | nvgBeginPath(args.vg); 123 | nvgRoundedRect(args.vg, 2 * padMargin, bounds[1] - (padMargin / 2.0), w - 4 * padMargin, 3 * (bounds[3] - bounds[1] + padMargin), 5.0); 124 | nvgFillColor(args.vg, bgColor); 125 | nvgFill(args.vg); 126 | } 127 | } 128 | 129 | void draw(const DrawArgs& args) override { drawLabel(args, 0.f, box.size.y, box.size.x, text.c_str(), textSize); } 130 | }; 131 | 132 | } // namespace TheModularMind -------------------------------------------------------------------------------- /src/components/MeowMory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../osc/OscController.hpp" 3 | 4 | namespace TheModularMind { 5 | 6 | struct ModuleMeowMoryParam { 7 | int paramId = -1; 8 | std::string address; 9 | int controllerId = -1; 10 | int encSensitivity = OscController::ENCODER_DEFAULT_SENSITIVITY; 11 | CONTROLLERMODE controllerMode; 12 | std::string label = ""; 13 | 14 | void fromMappings(ParamHandle paramHandle, OscController* oscController, std::string textLabel) { 15 | if (paramHandle.moduleId != -1) paramId = paramHandle.paramId; 16 | label = textLabel; 17 | 18 | if (oscController) { 19 | controllerId = oscController->getControllerId(); 20 | address = oscController->getAddress(); 21 | controllerMode = oscController->getControllerMode(); 22 | if (oscController->getSensitivity() != OscController::ENCODER_DEFAULT_SENSITIVITY) encSensitivity = oscController->getSensitivity(); 23 | } 24 | } 25 | 26 | void fromJson(json_t* meowMoryParamJ) { 27 | paramId = json_integer_value(json_object_get(meowMoryParamJ, "paramId")); 28 | 29 | json_t* labelJ = json_object_get(meowMoryParamJ, "label"); 30 | if (labelJ) label = json_string_value(labelJ); 31 | 32 | json_t* controllerIdJ = json_object_get(meowMoryParamJ, "controllerId"); 33 | if (controllerIdJ) { 34 | address = json_string_value(json_object_get(meowMoryParamJ, "address")); 35 | controllerMode = (CONTROLLERMODE)json_integer_value(json_object_get(meowMoryParamJ, "controllerMode")); 36 | controllerId = json_integer_value(controllerIdJ); 37 | 38 | json_t* encSensitivityJ = json_object_get(meowMoryParamJ, "encSensitivity"); 39 | if (encSensitivityJ) encSensitivity = json_integer_value(encSensitivityJ); 40 | } 41 | } 42 | 43 | json_t* toJson() { 44 | json_t* meowMoryParamJ = json_object(); 45 | if (paramId != -1) { 46 | json_object_set_new(meowMoryParamJ, "paramId", json_integer(paramId)); 47 | } 48 | if (controllerId != -1) { 49 | json_object_set_new(meowMoryParamJ, "controllerId", json_integer(controllerId)); 50 | json_object_set_new(meowMoryParamJ, "controllerMode", json_integer((int)controllerMode)); 51 | json_object_set_new(meowMoryParamJ, "address", json_string(address.c_str())); 52 | if (encSensitivity != OscController::ENCODER_DEFAULT_SENSITIVITY) json_object_set_new(meowMoryParamJ, "encSensitivity", json_integer(encSensitivity)); 53 | } 54 | if (label != "") json_object_set_new(meowMoryParamJ, "label", json_string(label.c_str())); 55 | 56 | return meowMoryParamJ; 57 | } 58 | }; 59 | 60 | struct BankMeowMoryParam : ModuleMeowMoryParam { 61 | int64_t moduleId = -1; 62 | 63 | void fromMappings(ParamHandle paramHandle, OscController* oscController, std::string textLabel) { 64 | ModuleMeowMoryParam::fromMappings(paramHandle, oscController, textLabel); 65 | if (paramHandle.moduleId != -1) moduleId = paramHandle.moduleId; 66 | } 67 | 68 | void fromJson(json_t* bankMeowMoryParamJ) { 69 | ModuleMeowMoryParam::fromJson(bankMeowMoryParamJ); 70 | moduleId = json_integer_value(json_object_get(bankMeowMoryParamJ, "moduleId")); 71 | } 72 | 73 | json_t* toJson() { 74 | json_t* bankMeowMoryParamJ = json_object(); 75 | bankMeowMoryParamJ = ModuleMeowMoryParam::toJson(); 76 | if (moduleId > 0) json_object_set_new(bankMeowMoryParamJ, "moduleId", json_integer(moduleId)); 77 | return bankMeowMoryParamJ; 78 | } 79 | }; 80 | 81 | struct ModuleMeowMory { 82 | std::string pluginName; 83 | std::string moduleName; 84 | std::list paramArray; 85 | 86 | ~ModuleMeowMory() { paramArray.clear(); } 87 | 88 | json_t* toJson() { 89 | json_t* meowMoryJ = json_object(); 90 | json_object_set_new(meowMoryJ, "pluginName", json_string(pluginName.c_str())); 91 | json_object_set_new(meowMoryJ, "moduleName", json_string(moduleName.c_str())); 92 | json_t* paramArrayJ = json_array(); 93 | for (auto& param : paramArray) { 94 | json_array_append_new(paramArrayJ, param.toJson()); 95 | } 96 | json_object_set_new(meowMoryJ, "params", paramArrayJ); 97 | return meowMoryJ; 98 | } 99 | 100 | void fromJson(json_t* meowMoryJ) { 101 | pluginName = json_string_value(json_object_get(meowMoryJ, "pluginName")); 102 | moduleName = json_string_value(json_object_get(meowMoryJ, "moduleName")); 103 | json_t* paramArrayJ = json_object_get(meowMoryJ, "params"); 104 | size_t j; 105 | json_t* meowMoryParamJ; 106 | json_array_foreach(paramArrayJ, j, meowMoryParamJ) { 107 | ModuleMeowMoryParam meowMoryParam = ModuleMeowMoryParam(); 108 | meowMoryParam.fromJson(meowMoryParamJ); 109 | paramArray.push_back(meowMoryParam); 110 | } 111 | } 112 | }; 113 | 114 | struct BankMeowMory { 115 | std::list bankParamArray; 116 | 117 | ~BankMeowMory() { bankParamArray.clear(); } 118 | 119 | json_t* toJson() { 120 | json_t* bankMeowMoryJ = json_object(); 121 | json_t* bankParamJ = json_object(); 122 | json_t* bankParamArrayJ = json_array(); 123 | for (auto& bankParam : bankParamArray) { 124 | bankParamJ = bankParam.toJson(); 125 | if (json_object_size(bankParamJ) > 0) json_array_append_new(bankParamArrayJ, bankParamJ); 126 | } 127 | if (json_array_size(bankParamArrayJ) > 0) { 128 | json_object_set_new(bankMeowMoryJ, "params", bankParamArrayJ); 129 | } 130 | return bankMeowMoryJ; 131 | } 132 | 133 | void fromJson(json_t* bankMeowMoryJ) { 134 | json_t* bankParamArrayJ = json_object_get(bankMeowMoryJ, "params"); 135 | size_t j; 136 | json_t* bankMeowMoryParamJ; 137 | json_array_foreach(bankParamArrayJ, j, bankMeowMoryParamJ) { 138 | BankMeowMoryParam bankMeowMoryParam; 139 | bankMeowMoryParam.fromJson(bankMeowMoryParamJ); 140 | bankParamArray.push_back(bankMeowMoryParam); 141 | } 142 | } 143 | }; 144 | 145 | } // namespace TheModularMind -------------------------------------------------------------------------------- /src/components/OscelotParam.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace TheModularMind { 4 | 5 | struct OscelotParam { 6 | ParamQuantity* paramQuantity = NULL; 7 | float limitMin; 8 | float limitMax; 9 | float uninit; 10 | float min = 0.f; 11 | float max = 1.f; 12 | 13 | float valueIn; 14 | float value; 15 | float valueOut; 16 | bool hasChanged; 17 | 18 | OscelotParam() { reset(); } 19 | 20 | bool isNear(float value, float jump = -1.0f) { 21 | if (value == -1.f) return false; 22 | float p = getValue(); 23 | float delta3p = (limitMax - limitMin + 1) * 0.01f; 24 | bool r = p - delta3p <= value && value <= p + delta3p; 25 | 26 | if (jump >= 0.f) { 27 | float delta7p = (limitMax - limitMin + 1) * 0.03f; 28 | r = r && p - delta7p <= jump && jump <= p + delta7p; 29 | } 30 | 31 | return r; 32 | } 33 | 34 | void setLimits(float min, float max, float uninit) { 35 | limitMin = min; 36 | limitMax = max; 37 | this->uninit = uninit; 38 | } 39 | float getLimitMin() { return limitMin; } 40 | float getLimitMax() { return limitMax; } 41 | 42 | void reset(bool resetSettings = true) { 43 | paramQuantity = NULL; 44 | valueIn = uninit; 45 | value = -1.f; 46 | valueOut = std::numeric_limits::infinity(); 47 | hasChanged = true; 48 | 49 | if (resetSettings) { 50 | min = 0.f; 51 | max = 1.f; 52 | } 53 | } 54 | 55 | void setParamQuantity(ParamQuantity* pq) { 56 | paramQuantity = pq; 57 | if (paramQuantity && valueOut == std::numeric_limits::infinity()) { 58 | valueOut = paramQuantity->getScaledValue(); 59 | } 60 | } 61 | 62 | void setMin(float v) { 63 | min = v; 64 | if (paramQuantity && valueIn != -1) setValue(valueIn); 65 | } 66 | float getMin() { return min; } 67 | 68 | void setMax(float v) { 69 | max = v; 70 | if (paramQuantity && valueIn != -1) setValue(valueIn); 71 | } 72 | float getMax() { return max; } 73 | 74 | void setValue(float i) { 75 | float f = rescale(i, limitMin, limitMax, min, max); 76 | f = clamp(f, 0.f, 1.f); 77 | valueIn = i; 78 | value = f; 79 | } 80 | 81 | void process(float sampleTime = -1.f, bool force = false) { 82 | if (valueOut == std::numeric_limits::infinity()) return; 83 | 84 | if (valueOut != value || force) { 85 | paramQuantity->setScaledValue(value); 86 | valueOut = value; 87 | } 88 | } 89 | 90 | float getValue() { 91 | float f = paramQuantity->getScaledValue(); 92 | if (valueOut == std::numeric_limits::infinity()) value = valueOut = f; 93 | f = rescale(f, min, max, limitMin, limitMax); 94 | f = clamp(f, limitMin, limitMax); 95 | if (valueIn == uninit) valueIn = f; 96 | return f; 97 | } 98 | }; // struct OscelotParam 99 | 100 | } // namespace TheModularMind -------------------------------------------------------------------------------- /src/components/ParamHandleIndicator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace TheModularMind { 4 | 5 | struct ParamHandleIndicator { 6 | ParamHandle* handle = NULL; 7 | NVGcolor color; 8 | 9 | int indicateCount = 0; 10 | float sampletime; 11 | 12 | void process(float sampleTime, bool force = false) { 13 | if (!handle) return; 14 | if (indicateCount > 0 || force) { 15 | this->sampletime += sampleTime; 16 | if (this->sampletime > 0.2f) { 17 | this->sampletime = 0; 18 | indicateCount--; 19 | handle->color = std::abs(indicateCount) % 2 == 1 ? color::BLACK : color; 20 | } 21 | } 22 | else { 23 | handle->color = color; 24 | } 25 | } 26 | 27 | void indicate(ModuleWidget* mw) { 28 | if (indicateCount > 0) return; 29 | if (mw) { 30 | // Move the view to center the mapped module 31 | TheModularMind::Rack::ViewportCenter{mw,1.5f}; 32 | } 33 | indicateCount = 20; 34 | } 35 | }; // struct ParamHandleIndicator 36 | 37 | } // namespace TheModularMind -------------------------------------------------------------------------------- /src/components/PawButtons.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "LedTextField.hpp" 3 | 4 | namespace TheModularMind { 5 | 6 | struct PawLight : SvgLight { 7 | std::shared_ptr svg; 8 | std::string svgPath; 9 | float degrees = 0.0; 10 | 11 | PawLight() { 12 | settings::haloBrightness = 0.1f; 13 | color = nvgRGB(0xff, 0xfa, 0xcd); 14 | this->addBaseColor(color); 15 | this->setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/paw.svg"))); 16 | } 17 | 18 | void draw(const DrawArgs& args) override { 19 | nvgBeginPath(args.vg); 20 | 21 | // Rotate Unlit SVG 22 | nvgTranslate(args.vg, box.size.x / 2.0, box.size.y / 2.0); 23 | nvgRotate(args.vg, nvgDegToRad(degrees)); 24 | nvgTranslate(args.vg, box.size.x / -2.0, box.size.y / -2.0); 25 | 26 | sw->draw(args); 27 | nvgFillColor(args.vg, color); 28 | nvgFill(args.vg); 29 | } 30 | 31 | void drawLight(const DrawArgs& args) override { 32 | // Foreground 33 | if (color.a > 0.0) { 34 | nvgBeginPath(args.vg); 35 | 36 | // Rotate Lit SVG 37 | nvgTranslate(args.vg, box.size.x / 2.0, box.size.y / 2.0); 38 | nvgRotate(args.vg, nvgDegToRad(degrees)); 39 | nvgTranslate(args.vg, box.size.x / -2.0, box.size.y / -2.0); 40 | 41 | sw->draw(args); 42 | nvgFillColor(args.vg, color); 43 | nvgFill(args.vg); 44 | } 45 | } 46 | }; 47 | 48 | struct PawNextLight : PawLight { 49 | PawNextLight() { degrees = 90.0; } 50 | }; 51 | 52 | struct PawPrevLight : PawLight { 53 | PawPrevLight() { degrees = -90.0; } 54 | }; 55 | 56 | struct PawButton : app::SvgSwitch { 57 | PawButton() { 58 | momentary = true; 59 | addFrame(Svg::load(asset::plugin(pluginInstance, "res/components/paw0.svg"))); 60 | addFrame(Svg::load(asset::plugin(pluginInstance, "res/components/paw1.svg"))); 61 | fb->removeChild(shadow); 62 | delete shadow; 63 | } 64 | }; 65 | 66 | struct PawScrew : app::SvgScrew { 67 | PawScrew() { setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/Screw.svg"))); } 68 | }; 69 | 70 | struct PawPort : app::SvgPort { 71 | PawPort() { setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/Port.svg"))); } 72 | }; 73 | 74 | struct LabelSliderHorizontal : app::SvgSlider { 75 | OscelotTextLabel* label; 76 | 77 | LabelSliderHorizontal() { 78 | horizontal = true; 79 | snap = true; 80 | setHandleSvg(Svg::load(asset::plugin(pluginInstance, "res/components/SliderHandle.svg"))); 81 | setBackgroundSvg(Svg::load(asset::plugin(pluginInstance, "res/components/SliderHorizontal.svg"))); 82 | minHandlePos = math::Vec(1, box.size.y / 2.0f - 12); 83 | maxHandlePos = math::Vec(box.size.x - handle->box.size.x - 1, box.size.y / 2.0f - 12); 84 | 85 | label = new OscelotTextLabel(); 86 | label->box.size = mm2px(Vec(0.0, -3.3)); 87 | label->textSize = 10.0f; 88 | label->drawBackground = false; 89 | this->addChild(label); 90 | } 91 | 92 | void step() override { 93 | SvgSlider::step(); 94 | // Move the center of the label to the center of the handle 95 | label->box.pos = this->handle->box.pos.plus(this->handle->box.size.div(2)).minus(label->box.size.div(2)); 96 | } 97 | }; 98 | 99 | } // namespace TheModularMind -------------------------------------------------------------------------------- /src/helpers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "plugin.hpp" 3 | #include "settings.hpp" 4 | 5 | namespace TheModularMind { 6 | namespace Rack { 7 | 8 | /** Move the view-port smoothly and center a Widget 9 | */ 10 | struct ViewportCenter { 11 | ViewportCenter(Widget* w, float zoomToWidget = -1.f) { 12 | // NB: unstable API! 13 | Vec target = w->box.pos; 14 | target = target.plus(w->box.size.mult(0.5f)); 15 | target = target.mult(APP->scene->rackScroll->zoomWidget->zoom); 16 | target = target.minus(APP->scene->rackScroll->box.size.mult(0.5f)); 17 | APP->scene->rackScroll->offset = target; 18 | if (zoomToWidget > 0.f) { 19 | APP->scene->rackScroll->setZoom(std::log2(APP->scene->rackScroll->box.size.y / w->box.size.y * zoomToWidget)); 20 | } 21 | } 22 | 23 | ViewportCenter(Vec target) { 24 | // NB: unstable API! 25 | target = target.mult(APP->scene->rackScroll->zoomWidget->zoom); 26 | target = target.minus(APP->scene->rackScroll->box.size.mult(0.5f)); 27 | APP->scene->rackScroll->offset = target; 28 | } 29 | 30 | ViewportCenter(Rect rect) { 31 | // NB: unstable API! 32 | Vec target = rect.getCenter(); 33 | target = target.mult(APP->scene->rackScroll->zoomWidget->zoom); 34 | target = target.minus(APP->scene->rackScroll->box.size.mult(0.5f)); 35 | APP->scene->rackScroll->offset = target; 36 | float zx = std::log2(APP->scene->rackScroll->box.size.x / rect.size.x * 0.9f); 37 | float zy = std::log2(APP->scene->rackScroll->box.size.y / rect.size.y * 0.9f); 38 | APP->scene->rackScroll->setZoom(std::min(zx, zy)); 39 | } 40 | }; 41 | 42 | } // namespace Rack 43 | } // namespace TheModularMind -------------------------------------------------------------------------------- /src/osc/OscArgs.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "oscpack/osc/OscTypes.h" 3 | 4 | namespace TheModularMind { 5 | 6 | class OscArg { 7 | public: 8 | virtual ~OscArg() {} 9 | virtual osc::TypeTagValues getType() const { return osc::NIL_TYPE_TAG; } 10 | }; 11 | 12 | class OscArgInt32 : public OscArg { 13 | public: 14 | OscArgInt32(std::int32_t value) : value(value) {} 15 | osc::TypeTagValues getType() const override { return osc::INT32_TYPE_TAG; } 16 | std::int32_t get() const { return value; } 17 | void set(std::int32_t value) { this->value = value; } 18 | 19 | private: 20 | std::int32_t value; 21 | }; 22 | 23 | class OscArgFloat : public OscArg { 24 | public: 25 | OscArgFloat(float value) : value(value) {} 26 | osc::TypeTagValues getType() const override { return osc::FLOAT_TYPE_TAG; } 27 | float get() const { return value; } 28 | void set(float value) { this->value = value; } 29 | 30 | private: 31 | float value; 32 | }; 33 | 34 | class OscArgString : public OscArg { 35 | public: 36 | OscArgString(const std::string &value) : value(value) {} 37 | osc::TypeTagValues getType() const override { return osc::STRING_TYPE_TAG; } 38 | const std::string &get() const { return value; } 39 | void set(const std::string &value) { this->value = value; } 40 | void set(const char *value) { this->value = value; } 41 | 42 | private: 43 | std::string value; 44 | }; 45 | } // namespace TheModularMind -------------------------------------------------------------------------------- /src/osc/OscBundle.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "OscMessage.hpp" 3 | 4 | namespace TheModularMind { 5 | 6 | class OscBundle { 7 | public: 8 | OscBundle() {} 9 | 10 | OscBundle(const OscBundle &oscBundle) { copy(oscBundle); } 11 | 12 | OscBundle &operator=(const OscBundle &oscBundle) { return copy(oscBundle); } 13 | 14 | OscBundle ©(const OscBundle &oscBundle) { 15 | if (this == &oscBundle) return *this; 16 | std::copy(oscBundle.bundles.begin(), oscBundle.bundles.end(), std::back_inserter(bundles)); 17 | std::copy(oscBundle.messages.begin(), oscBundle.messages.end(), std::back_inserter(messages)); 18 | return *this; 19 | } 20 | 21 | void clear() { 22 | bundles.clear(); 23 | messages.clear(); 24 | } 25 | 26 | void addBundle(const OscBundle &bundle) { bundles.push_back(bundle); } 27 | void addMessage(const OscMessage &message) { messages.push_back(message); } 28 | int getBundleCount() const { return bundles.size(); } 29 | int getMessageCount() const { return messages.size(); } 30 | const OscBundle &getBundleAt(std::size_t i) const { return bundles[i]; } 31 | const OscMessage &getMessageAt(std::size_t i) const { return messages[i]; } 32 | 33 | private: 34 | std::vector messages; 35 | std::vector bundles; 36 | }; 37 | } // namespace TheModularMind -------------------------------------------------------------------------------- /src/osc/OscController.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../plugin.hpp" 3 | 4 | namespace TheModularMind { 5 | 6 | enum class CONTROLLERMODE { DIRECT = 0, PICKUP1 = 1, PICKUP2 = 2, TOGGLE = 3, TOGGLE_VALUE = 4 }; 7 | 8 | class OscController { 9 | public: 10 | static OscController *Create(std::string address, int controllerId, CONTROLLERMODE controllerMode = CONTROLLERMODE::DIRECT, float value = -1.f, uint32_t ts = 0); 11 | static const int ENCODER_DEFAULT_SENSITIVITY = 649; 12 | 13 | virtual ~OscController() {} 14 | 15 | float getCurrentValue() { return current; } 16 | 17 | virtual bool setCurrentValue(float value, uint32_t ts) { 18 | current = value; 19 | lastTs = ts; 20 | return true; 21 | } 22 | 23 | void reset() { 24 | controllerId = -1; 25 | lastTs = 0; 26 | current = -1.0f; 27 | lastValueIn = -1.f; 28 | lastValueIndicate = -1.f; 29 | lastValueOut = -1.f; 30 | } 31 | 32 | void resetValue() { current = -1.0f; } 33 | virtual void setSensitivity(int sensitivity) {} 34 | virtual int getSensitivity() { return ENCODER_DEFAULT_SENSITIVITY; } 35 | int getControllerId() { return controllerId; } 36 | void setControllerId(int controllerId) { this->controllerId = controllerId; } 37 | void setTs(uint32_t ts) { this->lastTs = ts; } 38 | uint32_t getTs() { return lastTs; } 39 | void setAddress(std::string address) { this->address = address; } 40 | std::string getAddress() { return address; } 41 | const char *getTypeString() { return type; } 42 | void setTypeString(const char *type) { this->type = type; } 43 | void setControllerMode(CONTROLLERMODE controllerMode) { this->controllerMode = controllerMode; } 44 | CONTROLLERMODE getControllerMode() { return controllerMode; } 45 | 46 | void setValueIn(float value) { lastValueIn = value; } 47 | float getValueIn() { return lastValueIn; } 48 | void setValueOut(std::string value) { lastValueOut = value; } 49 | std::string getValueOut() { return lastValueOut; } 50 | void setValueIndicate(float value) { lastValueIndicate = value; } 51 | float getValueIndicate() { return lastValueIndicate; } 52 | 53 | private: 54 | int controllerId = -1; 55 | uint32_t lastTs = 0; 56 | float current; 57 | std::string address; 58 | const char *type; 59 | CONTROLLERMODE controllerMode; 60 | 61 | float lastValueIn = -1.f; 62 | float lastValueIndicate = -1.f; 63 | std::string lastValueOut = "-1"; 64 | }; 65 | 66 | } // namespace TheModularMind -------------------------------------------------------------------------------- /src/osc/OscMessage.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "OscArgs.hpp" 3 | 4 | namespace TheModularMind { 5 | 6 | class OscMessage { 7 | public: 8 | OscMessage() : remoteHost(""), remotePort(0) {} 9 | 10 | OscMessage(const OscMessage &oscMessage) { copy(oscMessage); } 11 | 12 | ~OscMessage() { clear(); } 13 | 14 | OscMessage &operator=(const OscMessage &oscMessage) { return copy(oscMessage); } 15 | 16 | OscMessage ©(const OscMessage &oscMessage) { 17 | if (this == &oscMessage) return *this; 18 | clear(); 19 | 20 | address = oscMessage.address; 21 | remoteHost = oscMessage.remoteHost; 22 | remotePort = oscMessage.remotePort; 23 | 24 | for (std::size_t i = 0; i < oscMessage.args.size(); ++i) { 25 | switch (oscMessage.getArgType(i)) { 26 | case osc::INT32_TYPE_TAG: 27 | args.push_back(new OscArgInt32(oscMessage.getArgAsInt(i))); 28 | break; 29 | case osc::FLOAT_TYPE_TAG: 30 | args.push_back(new OscArgFloat(oscMessage.getArgAsFloat(i))); 31 | break; 32 | case osc::STRING_TYPE_TAG: 33 | args.push_back(new OscArgString(oscMessage.getArgAsString(i))); 34 | break; 35 | default: 36 | FATAL("OscMessage copy(): bad/unimplemented argument type %i", oscMessage.getArgType(i)); 37 | break; 38 | } 39 | } 40 | return *this; 41 | } 42 | 43 | void clear() { 44 | address = ""; 45 | remoteHost = ""; 46 | remotePort = 0; 47 | for (unsigned int i = 0; i < args.size(); ++i) { 48 | delete args[i]; 49 | } 50 | args.clear(); 51 | } 52 | 53 | void setRemoteEndpoint(const std::string &host, int port) { 54 | remoteHost = host; 55 | remotePort = port; 56 | } 57 | 58 | osc::TypeTagValues getArgType(std::size_t index) const { 59 | if (index >= args.size()) { 60 | FATAL("OscMessage.getArgType(): index %lld out of bounds", index); 61 | return osc::NIL_TYPE_TAG; 62 | } else { 63 | return args[index]->getType(); 64 | } 65 | } 66 | 67 | void setAddress(const std::string &address) { this->address = address; } 68 | std::string getAddress() const { return address; } 69 | std::string getRemoteHost() const { return remoteHost; } 70 | int getRemotePort() const { return remotePort; } 71 | std::size_t getNumArgs() const { return args.size(); } 72 | 73 | std::int32_t getArgAsInt(std::size_t index) const { return ((OscArgInt32 *)args[index])->get(); } 74 | float getArgAsFloat(std::size_t index) const { return ((OscArgFloat *)args[index])->get(); } 75 | std::string getArgAsString(std::size_t index) const { return ((OscArgString *)args[index])->get(); } 76 | 77 | void addOscArg(OscArg* argument) { args.push_back(argument); } 78 | void addIntArg(std::int32_t argument) { args.push_back(new OscArgInt32(argument)); } 79 | void addFloatArg(float argument) { args.push_back(new OscArgFloat(argument)); } 80 | void addStringArg(const std::string &argument) { args.push_back(new OscArgString(argument)); } 81 | 82 | private: 83 | std::string address; 84 | std::vector args; 85 | std::string remoteHost; 86 | int remotePort; 87 | }; 88 | } // namespace TheModularMind -------------------------------------------------------------------------------- /src/osc/OscReceiver.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "oscpack/osc/OscPacketListener.h" 5 | 6 | namespace TheModularMind { 7 | 8 | struct OscReceiver : public osc::OscPacketListener { 9 | public: 10 | int port; 11 | 12 | OscReceiver() {} 13 | 14 | ~OscReceiver() { stop(); } 15 | 16 | bool start(int port) { 17 | if (listenSocket) { 18 | stop(); 19 | } 20 | this->port = port; 21 | 22 | UdpListeningReceiveSocket *socket = nullptr; 23 | try { 24 | IpEndpointName name(IpEndpointName::ANY_ADDRESS, port); 25 | socket = new UdpListeningReceiveSocket(name, this); 26 | 27 | // Socket deleter 28 | auto deleter = [](UdpListeningReceiveSocket *socket) { 29 | socket->Break(); 30 | delete socket; 31 | }; 32 | 33 | listenSocket = std::unique_ptr(socket, deleter); 34 | 35 | } catch (std::exception &e) { 36 | FATAL("OscReceiver couldn't create receiver on port %i, %s", port, e.what()); 37 | if (socket != nullptr) { 38 | delete socket; 39 | socket = nullptr; 40 | } 41 | return false; 42 | } 43 | 44 | listenThread = std::thread([this] { this->listenerProcess(); }); 45 | listenThread.detach(); 46 | return true; 47 | } 48 | 49 | void listenerProcess() { 50 | while (listenSocket) { 51 | try { 52 | listenSocket->Run(); 53 | } catch (std::exception &e) { 54 | FATAL("OscReceiver error: %s", e.what()); 55 | } 56 | } 57 | } 58 | 59 | void stop() { listenSocket.reset(); } 60 | 61 | bool shift(OscMessage *message) { 62 | if (!message) return false; 63 | if (!queue.empty()) { 64 | *message = queue.front(); 65 | queue.pop(); 66 | return true; 67 | } 68 | return false; 69 | } 70 | 71 | protected: 72 | /// process incoming OSC message and add it to the queue 73 | virtual void ProcessMessage(const osc::ReceivedMessage &receivedMessage, const IpEndpointName &remoteEndpoint) override { 74 | OscMessage msg; 75 | char endpointHost[IpEndpointName::ADDRESS_STRING_LENGTH]; 76 | 77 | remoteEndpoint.AddressAsString(endpointHost); 78 | msg.setAddress(receivedMessage.AddressPattern()); 79 | msg.setRemoteEndpoint(endpointHost, remoteEndpoint.port); 80 | 81 | for (auto arg = receivedMessage.ArgumentsBegin(); arg != receivedMessage.ArgumentsEnd(); ++arg) { 82 | if (arg->IsInt32()) { 83 | msg.addIntArg(arg->AsInt32Unchecked()); 84 | } else if (arg->IsFloat()) { 85 | msg.addFloatArg(arg->AsFloatUnchecked()); 86 | } else if (arg->IsString()) { 87 | msg.addStringArg(arg->AsStringUnchecked()); 88 | } else { 89 | FATAL("OscReceiver ProcessMessage(): argument in message %s is an unknown type %d", receivedMessage.AddressPattern(), arg->TypeTag()); 90 | break; 91 | } 92 | } 93 | queue.push(msg); 94 | } 95 | 96 | private: 97 | std::unique_ptr> listenSocket; 98 | std::queue queue; 99 | std::thread listenThread; 100 | }; 101 | } // namespace TheModularMind -------------------------------------------------------------------------------- /src/osc/OscSender.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "OscBundle.hpp" 3 | #include "oscpack/ip/UdpSocket.h" 4 | #include "oscpack/osc/OscOutboundPacketStream.h" 5 | 6 | namespace TheModularMind { 7 | 8 | class OscSender { 9 | public: 10 | std::string host; 11 | int port = 0; 12 | 13 | OscSender() {} 14 | 15 | ~OscSender() { stop(); } 16 | 17 | bool start(std::string &host, int port) { 18 | this->host = host; 19 | this->port = port; 20 | if (host == "") { 21 | host = "localhost"; 22 | } 23 | 24 | UdpTransmitSocket *socket = nullptr; 25 | try { 26 | IpEndpointName name = IpEndpointName(host.c_str(), port); 27 | if (!name.address) { 28 | FATAL("Bad hostname: %s", host.c_str()); 29 | return false; 30 | } 31 | socket = new UdpTransmitSocket(name); 32 | sendSocket.reset(socket); 33 | 34 | } catch (std::exception &e) { 35 | FATAL("OscSender couldn't start with %s:%i because of: %s", host.c_str(), port, e.what()); 36 | if (socket != nullptr) { 37 | delete socket; 38 | socket = nullptr; 39 | } 40 | sendSocket.reset(); 41 | return false; 42 | } 43 | return true; 44 | } 45 | 46 | void stop() { sendSocket.reset(); } 47 | 48 | void sendBundle(const OscBundle &bundle) { 49 | if (!sendSocket) { 50 | FATAL("OscSender trying to send with empty socket"); 51 | return; 52 | } 53 | 54 | static const int OUTPUT_BUFFER_SIZE = 327680; 55 | char buffer[OUTPUT_BUFFER_SIZE]; 56 | osc::OutboundPacketStream outputStream(buffer, OUTPUT_BUFFER_SIZE); 57 | appendBundle(bundle, outputStream); 58 | sendSocket->Send(outputStream.Data(), outputStream.Size()); 59 | } 60 | 61 | void sendMessage(const OscMessage &message) { 62 | if (!sendSocket) { 63 | FATAL("OscSender trying to send with empty socket"); 64 | return; 65 | } 66 | 67 | static const int OUTPUT_BUFFER_SIZE = 327680; 68 | char buffer[OUTPUT_BUFFER_SIZE]; 69 | osc::OutboundPacketStream outputStream(buffer, OUTPUT_BUFFER_SIZE); 70 | appendMessage(message, outputStream); 71 | sendSocket->Send(outputStream.Data(), outputStream.Size()); 72 | } 73 | 74 | private: 75 | std::unique_ptr sendSocket; 76 | 77 | void appendBundle(const OscBundle &bundle, osc::OutboundPacketStream &outputStream) { 78 | outputStream << osc::BeginBundleImmediate; 79 | for (int i = 0; i < bundle.getBundleCount(); i++) { 80 | appendBundle(bundle.getBundleAt(i), outputStream); 81 | } 82 | for (int i = 0; i < bundle.getMessageCount(); i++) { 83 | appendMessage(bundle.getMessageAt(i), outputStream); 84 | } 85 | outputStream << osc::EndBundle; 86 | } 87 | 88 | void appendMessage(const OscMessage &message, osc::OutboundPacketStream &outputStream) { 89 | outputStream << osc::BeginMessage(message.getAddress().c_str()); 90 | for (size_t i = 0; i < message.getNumArgs(); ++i) { 91 | switch (message.getArgType(i)) { 92 | case osc::INT32_TYPE_TAG: 93 | outputStream << message.getArgAsInt(i); 94 | break; 95 | case osc::FLOAT_TYPE_TAG: 96 | outputStream << message.getArgAsFloat(i); 97 | break; 98 | case osc::STRING_TYPE_TAG: 99 | outputStream << message.getArgAsString(i).c_str(); 100 | break; 101 | default: 102 | FATAL("OscSender.appendMessage(), Unimplemented type?: %i", (int)message.getArgType(i)); 103 | break; 104 | } 105 | } 106 | outputStream << osc::EndMessage; 107 | } 108 | }; 109 | } // namespace TheModularMind -------------------------------------------------------------------------------- /src/osc/oscpack/LICENSE: -------------------------------------------------------------------------------- 1 | oscpack -- Open Sound Control (OSC) packet manipulation library 2 | http://www.rossbencina.com/code/oscpack 3 | 4 | Copyright (c) 2004-2013 Ross Bencina 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files 8 | (the "Software"), to deal in the Software without restriction, 9 | including without limitation the rights to use, copy, modify, merge, 10 | publish, distribute, sublicense, and/or sell copies of the Software, 11 | and to permit persons to whom the Software is furnished to do so, 12 | subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 21 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 22 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | ### 26 | 27 | The text above constitutes the entire oscpack license; however, 28 | the oscpack developer(s) also make the following non-binding requests: 29 | 30 | Any person wishing to distribute modifications to the Software is 31 | requested to send the modifications to the original developer so that 32 | they can be incorporated into the canonical version. It is also 33 | requested that these non-binding requests be included whenever the 34 | above license is reproduced. -------------------------------------------------------------------------------- /src/osc/oscpack/README.md: -------------------------------------------------------------------------------- 1 | # oscpack -- Open Sound Control packet manipulation library 2 | A simple C++ library for packing and unpacking OSC packets. 3 | http://www.rossbencina.com/code/oscpack 4 | Copyright (c) 2004-2013 Ross Bencina 5 | 6 | Oscpack is simply a set of C++ classes for packing and unpacking OSC packets. 7 | Oscpack includes a minimal set of UDP networking classes for Windows and POSIX. 8 | The networking classes are sufficient for writing many OSC applications and servers, 9 | but you are encouraged to use another networking framework if it better suits your needs. 10 | Oscpack is not an OSC application framework. It doesn't include infrastructure for 11 | constructing or routing OSC namespaces, just classes for easily constructing, 12 | sending, receiving and parsing OSC packets. The library should also be easy to use 13 | for other transport methods (e.g. serial). 14 | 15 | The key goals of the oscpack library are: 16 | 17 | - Be a simple and complete implementation of OSC 18 | - Be portable to a wide variety of platforms 19 | - Allow easy development of robust OSC applications 20 | (for example it should be impossible to crash a server 21 | by sending it malformed packets, and difficult to create 22 | malformed packets.) 23 | 24 | --- 25 | If you fix anything or write a set of TCP send/receive classes please consider sending me a patch. 26 | My email address is rossb@audiomulch.com. Thanks :) 27 | 28 | For more information about Open Sound Control, see: 29 | http://opensoundcontrol.org/ 30 | 31 | Thanks to Till Bovermann for helping with POSIX networking code and 32 | Mac compatibility, and to Martin Kaltenbrunner and the rest of the 33 | reacTable team for giving me a reason to finish this library. Thanks 34 | to Merlijn Blaauw for reviewing the interfaces. Thanks to Xavier Oliver 35 | for additional help with Linux builds and POSIX implementation details. 36 | 37 | Portions developed at the Music Technology Group, Audiovisual Institute, 38 | University Pompeu Fabra, Barcelona, during my stay as a visiting 39 | researcher, November 2004 - September 2005. 40 | 41 | Thanks to Syneme at the University of Calgary for providing financial 42 | support for the 1.1.0 update, December 2012 - March 2013. 43 | 44 | See the file LICENSE for information about distributing and using this code. -------------------------------------------------------------------------------- /src/osc/oscpack/ip/IpEndpointName.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | oscpack -- Open Sound Control (OSC) packet manipulation library 3 | http://www.rossbencina.com/code/oscpack 4 | 5 | Copyright (c) 2004-2013 Ross Bencina 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 22 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | /* 28 | The text above constitutes the entire oscpack license; however, 29 | the oscpack developer(s) also make the following non-binding requests: 30 | 31 | Any person wishing to distribute modifications to the Software is 32 | requested to send the modifications to the original developer so that 33 | they can be incorporated into the canonical version. It is also 34 | requested that these non-binding requests be included whenever the 35 | above license is reproduced. 36 | */ 37 | #include "IpEndpointName.h" 38 | 39 | #include 40 | 41 | #include "NetworkingUtils.h" 42 | 43 | 44 | unsigned long IpEndpointName::GetHostByName( const char *s ) 45 | { 46 | return ::GetHostByName(s); 47 | } 48 | 49 | 50 | void IpEndpointName::AddressAsString( char *s ) const 51 | { 52 | if( address == ANY_ADDRESS ){ 53 | std::sprintf( s, "" ); 54 | }else{ 55 | std::sprintf( s, "%d.%d.%d.%d", 56 | (int)((address >> 24) & 0xFF), 57 | (int)((address >> 16) & 0xFF), 58 | (int)((address >> 8) & 0xFF), 59 | (int)(address & 0xFF) ); 60 | } 61 | } 62 | 63 | 64 | void IpEndpointName::AddressAndPortAsString( char *s ) const 65 | { 66 | if( port == ANY_PORT ){ 67 | if( address == ANY_ADDRESS ){ 68 | std::sprintf( s, ":" ); 69 | }else{ 70 | std::sprintf( s, "%d.%d.%d.%d:", 71 | (int)((address >> 24) & 0xFF), 72 | (int)((address >> 16) & 0xFF), 73 | (int)((address >> 8) & 0xFF), 74 | (int)(address & 0xFF) ); 75 | } 76 | }else{ 77 | if( address == ANY_ADDRESS ){ 78 | std::sprintf( s, ":%d", port ); 79 | }else{ 80 | std::sprintf( s, "%d.%d.%d.%d:%d", 81 | (int)((address >> 24) & 0xFF), 82 | (int)((address >> 16) & 0xFF), 83 | (int)((address >> 8) & 0xFF), 84 | (int)(address & 0xFF), 85 | (int)port ); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/osc/oscpack/ip/IpEndpointName.h: -------------------------------------------------------------------------------- 1 | /* 2 | oscpack -- Open Sound Control (OSC) packet manipulation library 3 | http://www.rossbencina.com/code/oscpack 4 | 5 | Copyright (c) 2004-2013 Ross Bencina 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 22 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | /* 28 | The text above constitutes the entire oscpack license; however, 29 | the oscpack developer(s) also make the following non-binding requests: 30 | 31 | Any person wishing to distribute modifications to the Software is 32 | requested to send the modifications to the original developer so that 33 | they can be incorporated into the canonical version. It is also 34 | requested that these non-binding requests be included whenever the 35 | above license is reproduced. 36 | */ 37 | #ifndef INCLUDED_OSCPACK_IPENDPOINTNAME_H 38 | #define INCLUDED_OSCPACK_IPENDPOINTNAME_H 39 | 40 | 41 | class IpEndpointName{ 42 | static unsigned long GetHostByName( const char *s ); 43 | public: 44 | static const unsigned long ANY_ADDRESS = 0xFFFFFFFF; 45 | static const int ANY_PORT = -1; 46 | 47 | IpEndpointName() 48 | : address( ANY_ADDRESS ), port( ANY_PORT ) {} 49 | IpEndpointName( int port_ ) 50 | : address( ANY_ADDRESS ), port( port_ ) {} 51 | IpEndpointName( unsigned long ipAddress_, int port_ ) 52 | : address( ipAddress_ ), port( port_ ) {} 53 | IpEndpointName( const char *addressName, int port_=ANY_PORT ) 54 | : address( GetHostByName( addressName ) ) 55 | , port( port_ ) {} 56 | IpEndpointName( int addressA, int addressB, int addressC, int addressD, int port_=ANY_PORT ) 57 | : address( ( (addressA << 24) | (addressB << 16) | (addressC << 8) | addressD ) ) 58 | , port( port_ ) {} 59 | 60 | // address and port are maintained in host byte order here 61 | unsigned long address; 62 | int port; 63 | 64 | bool IsMulticastAddress() const { return ((address >> 24) & 0xFF) >= 224 && ((address >> 24) & 0xFF) <= 239; } 65 | 66 | enum { ADDRESS_STRING_LENGTH=17 }; 67 | void AddressAsString( char *s ) const; 68 | 69 | enum { ADDRESS_AND_PORT_STRING_LENGTH=23}; 70 | void AddressAndPortAsString( char *s ) const; 71 | }; 72 | 73 | inline bool operator==( const IpEndpointName& lhs, const IpEndpointName& rhs ) 74 | { 75 | return (lhs.address == rhs.address && lhs.port == rhs.port ); 76 | } 77 | 78 | inline bool operator!=( const IpEndpointName& lhs, const IpEndpointName& rhs ) 79 | { 80 | return !(lhs == rhs); 81 | } 82 | 83 | #endif /* INCLUDED_OSCPACK_IPENDPOINTNAME_H */ 84 | -------------------------------------------------------------------------------- /src/osc/oscpack/ip/NetworkingUtils.h: -------------------------------------------------------------------------------- 1 | /* 2 | oscpack -- Open Sound Control (OSC) packet manipulation library 3 | http://www.rossbencina.com/code/oscpack 4 | 5 | Copyright (c) 2004-2013 Ross Bencina 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 22 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | /* 28 | The text above constitutes the entire oscpack license; however, 29 | the oscpack developer(s) also make the following non-binding requests: 30 | 31 | Any person wishing to distribute modifications to the Software is 32 | requested to send the modifications to the original developer so that 33 | they can be incorporated into the canonical version. It is also 34 | requested that these non-binding requests be included whenever the 35 | above license is reproduced. 36 | */ 37 | #ifndef INCLUDED_OSCPACK_NETWORKINGUTILS_H 38 | #define INCLUDED_OSCPACK_NETWORKINGUTILS_H 39 | 40 | 41 | // in general NetworkInitializer is only used internally, but if you're 42 | // application creates multiple sockets from different threads at runtime you 43 | // should instantiate one of these in main just to make sure the networking 44 | // layer is initialized. 45 | class NetworkInitializer{ 46 | public: 47 | NetworkInitializer(); 48 | ~NetworkInitializer(); 49 | }; 50 | 51 | 52 | // return ip address of host name in host byte order 53 | unsigned long GetHostByName( const char *name ); 54 | 55 | 56 | #endif /* INCLUDED_OSCPACK_NETWORKINGUTILS_H */ 57 | -------------------------------------------------------------------------------- /src/osc/oscpack/ip/PacketListener.h: -------------------------------------------------------------------------------- 1 | /* 2 | oscpack -- Open Sound Control (OSC) packet manipulation library 3 | http://www.rossbencina.com/code/oscpack 4 | 5 | Copyright (c) 2004-2013 Ross Bencina 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 22 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | /* 28 | The text above constitutes the entire oscpack license; however, 29 | the oscpack developer(s) also make the following non-binding requests: 30 | 31 | Any person wishing to distribute modifications to the Software is 32 | requested to send the modifications to the original developer so that 33 | they can be incorporated into the canonical version. It is also 34 | requested that these non-binding requests be included whenever the 35 | above license is reproduced. 36 | */ 37 | #ifndef INCLUDED_OSCPACK_PACKETLISTENER_H 38 | #define INCLUDED_OSCPACK_PACKETLISTENER_H 39 | 40 | 41 | class IpEndpointName; 42 | 43 | class PacketListener{ 44 | public: 45 | virtual ~PacketListener() {} 46 | virtual void ProcessPacket( const char *data, int size, 47 | const IpEndpointName& remoteEndpoint ) = 0; 48 | }; 49 | 50 | #endif /* INCLUDED_OSCPACK_PACKETLISTENER_H */ 51 | -------------------------------------------------------------------------------- /src/osc/oscpack/ip/TimerListener.h: -------------------------------------------------------------------------------- 1 | /* 2 | oscpack -- Open Sound Control (OSC) packet manipulation library 3 | http://www.rossbencina.com/code/oscpack 4 | 5 | Copyright (c) 2004-2013 Ross Bencina 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 22 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | /* 28 | The text above constitutes the entire oscpack license; however, 29 | the oscpack developer(s) also make the following non-binding requests: 30 | 31 | Any person wishing to distribute modifications to the Software is 32 | requested to send the modifications to the original developer so that 33 | they can be incorporated into the canonical version. It is also 34 | requested that these non-binding requests be included whenever the 35 | above license is reproduced. 36 | */ 37 | #ifndef INCLUDED_OSCPACK_TIMERLISTENER_H 38 | #define INCLUDED_OSCPACK_TIMERLISTENER_H 39 | 40 | 41 | class TimerListener{ 42 | public: 43 | virtual ~TimerListener() {} 44 | virtual void TimerExpired() = 0; 45 | }; 46 | 47 | #endif /* INCLUDED_OSCPACK_TIMERLISTENER_H */ 48 | -------------------------------------------------------------------------------- /src/osc/oscpack/ip/UdpSocket.h: -------------------------------------------------------------------------------- 1 | /* 2 | oscpack -- Open Sound Control (OSC) packet manipulation library 3 | http://www.rossbencina.com/code/oscpack 4 | 5 | Copyright (c) 2004-2013 Ross Bencina 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 22 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | /* 28 | The text above constitutes the entire oscpack license; however, 29 | the oscpack developer(s) also make the following non-binding requests: 30 | 31 | Any person wishing to distribute modifications to the Software is 32 | requested to send the modifications to the original developer so that 33 | they can be incorporated into the canonical version. It is also 34 | requested that these non-binding requests be included whenever the 35 | above license is reproduced. 36 | */ 37 | #ifndef INCLUDED_OSCPACK_UDPSOCKET_H 38 | #define INCLUDED_OSCPACK_UDPSOCKET_H 39 | 40 | #include // size_t 41 | 42 | #include "NetworkingUtils.h" 43 | #include "IpEndpointName.h" 44 | 45 | 46 | class PacketListener; 47 | class TimerListener; 48 | 49 | class UdpSocket; 50 | 51 | class SocketReceiveMultiplexer{ 52 | class Implementation; 53 | Implementation *impl_; 54 | 55 | friend class UdpSocket; 56 | 57 | public: 58 | SocketReceiveMultiplexer(); 59 | ~SocketReceiveMultiplexer(); 60 | 61 | // only call the attach/detach methods _before_ calling Run 62 | 63 | // only one listener per socket, each socket at most once 64 | void AttachSocketListener( UdpSocket *socket, PacketListener *listener ); 65 | void DetachSocketListener( UdpSocket *socket, PacketListener *listener ); 66 | 67 | void AttachPeriodicTimerListener( int periodMilliseconds, TimerListener *listener ); 68 | void AttachPeriodicTimerListener( 69 | int initialDelayMilliseconds, int periodMilliseconds, TimerListener *listener ); 70 | void DetachPeriodicTimerListener( TimerListener *listener ); 71 | 72 | void Run(); // loop and block processing messages indefinitely 73 | void RunUntilSigInt(); 74 | void Break(); // call this from a listener to exit once the listener returns 75 | void AsynchronousBreak(); // call this from another thread or signal handler to exit the Run() state 76 | }; 77 | 78 | 79 | class UdpSocket{ 80 | class Implementation; 81 | Implementation *impl_; 82 | 83 | friend class SocketReceiveMultiplexer::Implementation; 84 | 85 | public: 86 | 87 | // Ctor throws std::runtime_error if there's a problem 88 | // initializing the socket. 89 | UdpSocket(); 90 | virtual ~UdpSocket(); 91 | 92 | // Enable broadcast addresses (e.g. x.x.x.255) 93 | // Sets SO_BROADCAST socket option. 94 | void SetEnableBroadcast( bool enableBroadcast ); 95 | 96 | // Enable multiple listeners for a single port on same 97 | // network interface* 98 | // Sets SO_REUSEADDR (also SO_REUSEPORT on OS X). 99 | // [*] The exact behavior of SO_REUSEADDR and 100 | // SO_REUSEPORT is undefined for some common cases 101 | // and may have drastically different behavior on different 102 | // operating systems. 103 | void SetAllowReuse( bool allowReuse ); 104 | 105 | 106 | // The socket is created in an unbound, unconnected state 107 | // such a socket can only be used to send to an arbitrary 108 | // address using SendTo(). To use Send() you need to first 109 | // connect to a remote endpoint using Connect(). To use 110 | // ReceiveFrom you need to first bind to a local endpoint 111 | // using Bind(). 112 | 113 | // Retrieve the local endpoint name when sending to 'to' 114 | IpEndpointName LocalEndpointFor( const IpEndpointName& remoteEndpoint ) const; 115 | 116 | // Connect to a remote endpoint which is used as the target 117 | // for calls to Send() 118 | void Connect( const IpEndpointName& remoteEndpoint ); 119 | void Send( const char *data, std::size_t size ); 120 | void SendTo( const IpEndpointName& remoteEndpoint, const char *data, std::size_t size ); 121 | 122 | 123 | // Bind a local endpoint to receive incoming data. Endpoint 124 | // can be 'any' for the system to choose an endpoint 125 | void Bind( const IpEndpointName& localEndpoint ); 126 | bool IsBound() const; 127 | 128 | std::size_t ReceiveFrom( IpEndpointName& remoteEndpoint, char *data, std::size_t size ); 129 | }; 130 | 131 | 132 | // convenience classes for transmitting and receiving 133 | // they just call Connect and/or Bind in the ctor. 134 | // note that you can still use a receive socket 135 | // for transmitting etc 136 | 137 | class UdpTransmitSocket : public UdpSocket{ 138 | public: 139 | UdpTransmitSocket( const IpEndpointName& remoteEndpoint ) 140 | { Connect( remoteEndpoint ); } 141 | }; 142 | 143 | 144 | class UdpReceiveSocket : public UdpSocket{ 145 | public: 146 | UdpReceiveSocket( const IpEndpointName& localEndpoint ) 147 | { Bind( localEndpoint ); } 148 | }; 149 | 150 | 151 | // UdpListeningReceiveSocket provides a simple way to bind one listener 152 | // to a single socket without having to manually set up a SocketReceiveMultiplexer 153 | 154 | class UdpListeningReceiveSocket : public UdpSocket{ 155 | SocketReceiveMultiplexer mux_; 156 | PacketListener *listener_; 157 | public: 158 | UdpListeningReceiveSocket( const IpEndpointName& localEndpoint, PacketListener *listener ) 159 | : listener_( listener ) 160 | { 161 | Bind( localEndpoint ); 162 | mux_.AttachSocketListener( this, listener_ ); 163 | } 164 | 165 | ~UdpListeningReceiveSocket() 166 | { mux_.DetachSocketListener( this, listener_ ); } 167 | 168 | // see SocketReceiveMultiplexer above for the behaviour of these methods... 169 | void Run() { mux_.Run(); } 170 | void RunUntilSigInt() { mux_.RunUntilSigInt(); } 171 | void Break() { mux_.Break(); } 172 | void AsynchronousBreak() { mux_.AsynchronousBreak(); } 173 | }; 174 | 175 | 176 | #endif /* INCLUDED_OSCPACK_UDPSOCKET_H */ 177 | -------------------------------------------------------------------------------- /src/osc/oscpack/ip/posix/NetworkingUtils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | oscpack -- Open Sound Control (OSC) packet manipulation library 3 | http://www.rossbencina.com/code/oscpack 4 | 5 | Copyright (c) 2004-2013 Ross Bencina 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 22 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | /* 28 | The text above constitutes the entire oscpack license; however, 29 | the oscpack developer(s) also make the following non-binding requests: 30 | 31 | Any person wishing to distribute modifications to the Software is 32 | requested to send the modifications to the original developer so that 33 | they can be incorporated into the canonical version. It is also 34 | requested that these non-binding requests be included whenever the 35 | above license is reproduced. 36 | */ 37 | #include "../NetworkingUtils.h" 38 | 39 | #include 40 | #include 41 | #include 42 | 43 | #include 44 | 45 | 46 | 47 | NetworkInitializer::NetworkInitializer() {} 48 | 49 | NetworkInitializer::~NetworkInitializer() {} 50 | 51 | 52 | unsigned long GetHostByName( const char *name ) 53 | { 54 | unsigned long result = 0; 55 | 56 | struct hostent *h = gethostbyname( name ); 57 | if( h ){ 58 | struct in_addr a; 59 | std::memcpy( &a, h->h_addr_list[0], h->h_length ); 60 | result = ntohl(a.s_addr); 61 | } 62 | 63 | return result; 64 | } 65 | -------------------------------------------------------------------------------- /src/osc/oscpack/ip/win32/NetworkingUtils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | oscpack -- Open Sound Control (OSC) packet manipulation library 3 | http://www.rossbencina.com/code/oscpack 4 | 5 | Copyright (c) 2004-2013 Ross Bencina 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 22 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | /* 28 | The text above constitutes the entire oscpack license; however, 29 | the oscpack developer(s) also make the following non-binding requests: 30 | 31 | Any person wishing to distribute modifications to the Software is 32 | requested to send the modifications to the original developer so that 33 | they can be incorporated into the canonical version. It is also 34 | requested that these non-binding requests be included whenever the 35 | above license is reproduced. 36 | */ 37 | #include "../NetworkingUtils.h" 38 | 39 | #include // this must come first to prevent errors with MSVC7 40 | #include 41 | 42 | #include 43 | 44 | 45 | static LONG initCount_ = 0; 46 | static bool winsockInitialized_ = false; 47 | 48 | NetworkInitializer::NetworkInitializer() 49 | { 50 | if( InterlockedIncrement( &initCount_ ) == 1 ){ 51 | // there is a race condition here if one thread tries to access 52 | // the library while another is still initializing it. 53 | // i can't think of an easy way to fix it so i'm telling you here 54 | // incase you need to init the library from two threads at once. 55 | // this is why the header file advises to instantiate one of these 56 | // in main() so that the initialization happens globally 57 | 58 | // initialize winsock 59 | WSAData wsaData; 60 | int nCode = WSAStartup(MAKEWORD(1, 1), &wsaData); 61 | if( nCode != 0 ){ 62 | //std::cout << "WSAStartup() failed with error code " << nCode << "\n"; 63 | }else{ 64 | winsockInitialized_ = true; 65 | } 66 | } 67 | } 68 | 69 | 70 | NetworkInitializer::~NetworkInitializer() 71 | { 72 | if( InterlockedDecrement( &initCount_ ) == 0 ){ 73 | if( winsockInitialized_ ){ 74 | WSACleanup(); 75 | winsockInitialized_ = false; 76 | } 77 | } 78 | } 79 | 80 | 81 | unsigned long GetHostByName( const char *name ) 82 | { 83 | NetworkInitializer networkInitializer; 84 | 85 | unsigned long result = 0; 86 | 87 | struct hostent *h = gethostbyname( name ); 88 | if( h ){ 89 | struct in_addr a; 90 | std::memcpy( &a, h->h_addr_list[0], h->h_length ); 91 | result = ntohl(a.s_addr); 92 | } 93 | 94 | return result; 95 | } 96 | -------------------------------------------------------------------------------- /src/osc/oscpack/osc/MessageMappingOscPacketListener.h: -------------------------------------------------------------------------------- 1 | /* 2 | oscpack -- Open Sound Control (OSC) packet manipulation library 3 | http://www.rossbencina.com/code/oscpack 4 | 5 | Copyright (c) 2004-2013 Ross Bencina 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 22 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | /* 28 | The text above constitutes the entire oscpack license; however, 29 | the oscpack developer(s) also make the following non-binding requests: 30 | 31 | Any person wishing to distribute modifications to the Software is 32 | requested to send the modifications to the original developer so that 33 | they can be incorporated into the canonical version. It is also 34 | requested that these non-binding requests be included whenever the 35 | above license is reproduced. 36 | */ 37 | #ifndef INCLUDED_OSCPACK_MESSAGEMAPPINGOSCPACKETLISTENER_H 38 | #define INCLUDED_OSCPACK_MESSAGEMAPPINGOSCPACKETLISTENER_H 39 | 40 | #include 41 | #include 42 | 43 | #include "OscPacketListener.h" 44 | 45 | 46 | 47 | namespace osc{ 48 | 49 | template< class T > 50 | class MessageMappingOscPacketListener : public OscPacketListener{ 51 | public: 52 | typedef void (T::*function_type)(const osc::ReceivedMessage&, const IpEndpointName&); 53 | 54 | protected: 55 | void RegisterMessageFunction( const char *addressPattern, function_type f ) 56 | { 57 | functions_.insert( std::make_pair( addressPattern, f ) ); 58 | } 59 | 60 | virtual void ProcessMessage( const osc::ReceivedMessage& m, 61 | const IpEndpointName& remoteEndpoint ) 62 | { 63 | typename function_map_type::iterator i = functions_.find( m.AddressPattern() ); 64 | if( i != functions_.end() ) 65 | (dynamic_cast(this)->*(i->second))( m, remoteEndpoint ); 66 | } 67 | 68 | private: 69 | struct cstr_compare{ 70 | bool operator()( const char *lhs, const char *rhs ) const 71 | { return std::strcmp( lhs, rhs ) < 0; } 72 | }; 73 | 74 | typedef std::map function_map_type; 75 | function_map_type functions_; 76 | }; 77 | 78 | } // namespace osc 79 | 80 | #endif /* INCLUDED_OSCPACK_MESSAGEMAPPINGOSCPACKETLISTENER_H */ -------------------------------------------------------------------------------- /src/osc/oscpack/osc/OscException.h: -------------------------------------------------------------------------------- 1 | /* 2 | oscpack -- Open Sound Control (OSC) packet manipulation library 3 | http://www.rossbencina.com/code/oscpack 4 | 5 | Copyright (c) 2004-2013 Ross Bencina 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 22 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | /* 28 | The text above constitutes the entire oscpack license; however, 29 | the oscpack developer(s) also make the following non-binding requests: 30 | 31 | Any person wishing to distribute modifications to the Software is 32 | requested to send the modifications to the original developer so that 33 | they can be incorporated into the canonical version. It is also 34 | requested that these non-binding requests be included whenever the 35 | above license is reproduced. 36 | */ 37 | #ifndef INCLUDED_OSCPACK_OSCEXCEPTION_H 38 | #define INCLUDED_OSCPACK_OSCEXCEPTION_H 39 | 40 | #include 41 | 42 | namespace osc{ 43 | 44 | class Exception : public std::exception { 45 | const char *what_; 46 | 47 | public: 48 | Exception() throw() {} 49 | Exception( const Exception& src ) throw() 50 | : std::exception( src ) 51 | , what_( src.what_ ) {} 52 | Exception( const char *w ) throw() 53 | : what_( w ) {} 54 | Exception& operator=( const Exception& src ) throw() 55 | { what_ = src.what_; return *this; } 56 | virtual ~Exception() throw() {} 57 | virtual const char* what() const throw() override { return what_; } 58 | }; 59 | 60 | } // namespace osc 61 | 62 | #endif /* INCLUDED_OSCPACK_OSCEXCEPTION_H */ 63 | -------------------------------------------------------------------------------- /src/osc/oscpack/osc/OscHostEndianness.h: -------------------------------------------------------------------------------- 1 | /* 2 | oscpack -- Open Sound Control (OSC) packet manipulation library 3 | http://www.rossbencina.com/code/oscpack 4 | 5 | Copyright (c) 2004-2013 Ross Bencina 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 22 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | /* 28 | The text above constitutes the entire oscpack license; however, 29 | the oscpack developer(s) also make the following non-binding requests: 30 | 31 | Any person wishing to distribute modifications to the Software is 32 | requested to send the modifications to the original developer so that 33 | they can be incorporated into the canonical version. It is also 34 | requested that these non-binding requests be included whenever the 35 | above license is reproduced. 36 | */ 37 | #ifndef INCLUDED_OSCPACK_OSCHOSTENDIANNESS_H 38 | #define INCLUDED_OSCPACK_OSCHOSTENDIANNESS_H 39 | 40 | /* 41 | Make sure either OSC_HOST_LITTLE_ENDIAN or OSC_HOST_BIG_ENDIAN is defined 42 | 43 | We try to use preprocessor symbols to deduce the host endianness. 44 | 45 | Alternatively you can define one of the above symbols from the command line. 46 | Usually you do this with the -D flag to the compiler. e.g.: 47 | 48 | $ g++ -DOSC_HOST_LITTLE_ENDIAN ... 49 | */ 50 | 51 | #if defined(OSC_HOST_LITTLE_ENDIAN) || defined(OSC_HOST_BIG_ENDIAN) 52 | 53 | // endianness defined on the command line. nothing to do here. 54 | 55 | #elif defined(__WIN32__) || defined(WIN32) || defined(WINCE) 56 | 57 | // assume that __WIN32__ is only defined on little endian systems 58 | 59 | #define OSC_HOST_LITTLE_ENDIAN 1 60 | #undef OSC_HOST_BIG_ENDIAN 61 | 62 | #elif defined(__APPLE__) 63 | 64 | #if defined(__LITTLE_ENDIAN__) 65 | 66 | #define OSC_HOST_LITTLE_ENDIAN 1 67 | #undef OSC_HOST_BIG_ENDIAN 68 | 69 | #elif defined(__BIG_ENDIAN__) 70 | 71 | #define OSC_HOST_BIG_ENDIAN 1 72 | #undef OSC_HOST_LITTLE_ENDIAN 73 | 74 | #endif 75 | 76 | #elif defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__) 77 | 78 | // should cover gcc and clang 79 | 80 | #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) 81 | 82 | #define OSC_HOST_LITTLE_ENDIAN 1 83 | #undef OSC_HOST_BIG_ENDIAN 84 | 85 | #elif (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) 86 | 87 | #define OSC_HOST_BIG_ENDIAN 1 88 | #undef OSC_HOST_LITTLE_ENDIAN 89 | 90 | #endif 91 | 92 | #else 93 | 94 | // gcc defines __LITTLE_ENDIAN__ and __BIG_ENDIAN__ 95 | // for others used here see http://sourceforge.net/p/predef/wiki/Endianness/ 96 | #if (defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)) \ 97 | || (defined(__ARMEL__) && !defined(__ARMEB__)) \ 98 | || (defined(__AARCH64EL__) && !defined(__AARCH64EB__)) \ 99 | || (defined(_MIPSEL) && !defined(_MIPSEB)) \ 100 | || (defined(__MIPSEL) && !defined(__MIPSEB)) \ 101 | || (defined(__MIPSEL__) && !defined(__MIPSEB__)) 102 | 103 | #define OSC_HOST_LITTLE_ENDIAN 1 104 | #undef OSC_HOST_BIG_ENDIAN 105 | 106 | #elif (defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)) \ 107 | || (defined(__ARMEB__) && !defined(__ARMEL__)) \ 108 | || (defined(__AARCH64EB__) && !defined(__AARCH64EL__)) \ 109 | || (defined(_MIPSEB) && !defined(_MIPSEL)) \ 110 | || (defined(__MIPSEB) && !defined(__MIPSEL)) \ 111 | || (defined(__MIPSEB__) && !defined(__MIPSEL__)) 112 | 113 | #define OSC_HOST_BIG_ENDIAN 1 114 | #undef OSC_HOST_LITTLE_ENDIAN 115 | 116 | #endif 117 | 118 | #endif 119 | 120 | #if !defined(OSC_HOST_LITTLE_ENDIAN) && !defined(OSC_HOST_BIG_ENDIAN) 121 | 122 | #error please edit OSCHostEndianness.h or define one of {OSC_HOST_LITTLE_ENDIAN, OSC_HOST_BIG_ENDIAN} to configure endianness 123 | 124 | #endif 125 | 126 | #endif /* INCLUDED_OSCPACK_OSCHOSTENDIANNESS_H */ 127 | 128 | -------------------------------------------------------------------------------- /src/osc/oscpack/osc/OscOutboundPacketStream.h: -------------------------------------------------------------------------------- 1 | /* 2 | oscpack -- Open Sound Control (OSC) packet manipulation library 3 | http://www.rossbencina.com/code/oscpack 4 | 5 | Copyright (c) 2004-2013 Ross Bencina 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 22 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | /* 28 | The text above constitutes the entire oscpack license; however, 29 | the oscpack developer(s) also make the following non-binding requests: 30 | 31 | Any person wishing to distribute modifications to the Software is 32 | requested to send the modifications to the original developer so that 33 | they can be incorporated into the canonical version. It is also 34 | requested that these non-binding requests be included whenever the 35 | above license is reproduced. 36 | */ 37 | #ifndef INCLUDED_OSCPACK_OSCOUTBOUNDPACKETSTREAM_H 38 | #define INCLUDED_OSCPACK_OSCOUTBOUNDPACKETSTREAM_H 39 | 40 | #include // size_t 41 | 42 | #include "OscTypes.h" 43 | #include "OscException.h" 44 | 45 | 46 | namespace osc{ 47 | 48 | class OutOfBufferMemoryException : public Exception{ 49 | public: 50 | OutOfBufferMemoryException( const char *w="out of buffer memory" ) 51 | : Exception( w ) {} 52 | }; 53 | 54 | class BundleNotInProgressException : public Exception{ 55 | public: 56 | BundleNotInProgressException( 57 | const char *w="call to EndBundle when bundle is not in progress" ) 58 | : Exception( w ) {} 59 | }; 60 | 61 | class MessageInProgressException : public Exception{ 62 | public: 63 | MessageInProgressException( 64 | const char *w="opening or closing bundle or message while message is in progress" ) 65 | : Exception( w ) {} 66 | }; 67 | 68 | class MessageNotInProgressException : public Exception{ 69 | public: 70 | MessageNotInProgressException( 71 | const char *w="call to EndMessage when message is not in progress" ) 72 | : Exception( w ) {} 73 | }; 74 | 75 | 76 | class OutboundPacketStream{ 77 | public: 78 | OutboundPacketStream( char *buffer, std::size_t capacity ); 79 | ~OutboundPacketStream(); 80 | 81 | void Clear(); 82 | 83 | std::size_t Capacity() const; 84 | 85 | // invariant: size() is valid even while building a message. 86 | std::size_t Size() const; 87 | 88 | const char *Data() const; 89 | 90 | // indicates that all messages have been closed with a matching EndMessage 91 | // and all bundles have been closed with a matching EndBundle 92 | bool IsReady() const; 93 | 94 | bool IsMessageInProgress() const; 95 | bool IsBundleInProgress() const; 96 | 97 | OutboundPacketStream& operator<<( const BundleInitiator& rhs ); 98 | OutboundPacketStream& operator<<( const BundleTerminator& rhs ); 99 | 100 | OutboundPacketStream& operator<<( const BeginMessage& rhs ); 101 | OutboundPacketStream& operator<<( const MessageTerminator& rhs ); 102 | 103 | OutboundPacketStream& operator<<( bool rhs ); 104 | OutboundPacketStream& operator<<( const NilType& rhs ); 105 | OutboundPacketStream& operator<<( const InfinitumType& rhs ); 106 | OutboundPacketStream& operator<<( int32 rhs ); 107 | 108 | #if !(defined(__x86_64__) || defined(_M_X64)) 109 | OutboundPacketStream& operator<<( int rhs ) 110 | { *this << (int32)rhs; return *this; } 111 | #endif 112 | 113 | OutboundPacketStream& operator<<( float rhs ); 114 | OutboundPacketStream& operator<<( char rhs ); 115 | OutboundPacketStream& operator<<( const RgbaColor& rhs ); 116 | OutboundPacketStream& operator<<( const MidiMessage& rhs ); 117 | OutboundPacketStream& operator<<( int64 rhs ); 118 | OutboundPacketStream& operator<<( const TimeTag& rhs ); 119 | OutboundPacketStream& operator<<( double rhs ); 120 | OutboundPacketStream& operator<<( const char* rhs ); 121 | OutboundPacketStream& operator<<( const Symbol& rhs ); 122 | OutboundPacketStream& operator<<( const Blob& rhs ); 123 | 124 | OutboundPacketStream& operator<<( const ArrayInitiator& rhs ); 125 | OutboundPacketStream& operator<<( const ArrayTerminator& rhs ); 126 | 127 | private: 128 | 129 | char *BeginElement( char *beginPtr ); 130 | void EndElement( char *endPtr ); 131 | 132 | bool ElementSizeSlotRequired() const; 133 | void CheckForAvailableBundleSpace(); 134 | void CheckForAvailableMessageSpace( const char *addressPattern ); 135 | void CheckForAvailableArgumentSpace( std::size_t argumentLength ); 136 | 137 | char *data_; 138 | char *end_; 139 | 140 | char *typeTagsCurrent_; // stored in reverse order 141 | char *messageCursor_; 142 | char *argumentCurrent_; 143 | 144 | // elementSizePtr_ has two special values: 0 indicates that a bundle 145 | // isn't open, and elementSizePtr_==data_ indicates that a bundle is 146 | // open but that it doesn't have a size slot (ie the outermost bundle) 147 | uint32 *elementSizePtr_; 148 | 149 | bool messageIsInProgress_; 150 | }; 151 | 152 | } // namespace osc 153 | 154 | #endif /* INCLUDED_OSCPACK_OSCOUTBOUNDPACKETSTREAM_H */ 155 | -------------------------------------------------------------------------------- /src/osc/oscpack/osc/OscPacketListener.h: -------------------------------------------------------------------------------- 1 | /* 2 | oscpack -- Open Sound Control (OSC) packet manipulation library 3 | http://www.rossbencina.com/code/oscpack 4 | 5 | Copyright (c) 2004-2013 Ross Bencina 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 22 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | /* 28 | The text above constitutes the entire oscpack license; however, 29 | the oscpack developer(s) also make the following non-binding requests: 30 | 31 | Any person wishing to distribute modifications to the Software is 32 | requested to send the modifications to the original developer so that 33 | they can be incorporated into the canonical version. It is also 34 | requested that these non-binding requests be included whenever the 35 | above license is reproduced. 36 | */ 37 | #ifndef INCLUDED_OSCPACK_OSCPACKETLISTENER_H 38 | #define INCLUDED_OSCPACK_OSCPACKETLISTENER_H 39 | 40 | #include "OscReceivedElements.h" 41 | #include "../ip/PacketListener.h" 42 | 43 | 44 | namespace osc{ 45 | 46 | class OscPacketListener : public PacketListener{ 47 | protected: 48 | virtual void ProcessBundle( const osc::ReceivedBundle& b, 49 | const IpEndpointName& remoteEndpoint ) 50 | { 51 | // ignore bundle time tag for now 52 | 53 | for( ReceivedBundle::const_iterator i = b.ElementsBegin(); 54 | i != b.ElementsEnd(); ++i ){ 55 | if( i->IsBundle() ) 56 | ProcessBundle( ReceivedBundle(*i), remoteEndpoint ); 57 | else 58 | ProcessMessage( ReceivedMessage(*i), remoteEndpoint ); 59 | } 60 | } 61 | 62 | virtual void ProcessMessage( const osc::ReceivedMessage& m, 63 | const IpEndpointName& remoteEndpoint ) = 0; 64 | 65 | public: 66 | virtual void ProcessPacket( const char *data, int size, 67 | const IpEndpointName& remoteEndpoint ) override 68 | { 69 | osc::ReceivedPacket p( data, size ); 70 | if( p.IsBundle() ) 71 | ProcessBundle( ReceivedBundle(p), remoteEndpoint ); 72 | else 73 | ProcessMessage( ReceivedMessage(p), remoteEndpoint ); 74 | } 75 | }; 76 | 77 | } // namespace osc 78 | 79 | #endif /* INCLUDED_OSCPACK_OSCPACKETLISTENER_H */ 80 | -------------------------------------------------------------------------------- /src/osc/oscpack/osc/OscPrintReceivedElements.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | oscpack -- Open Sound Control (OSC) packet manipulation library 3 | http://www.rossbencina.com/code/oscpack 4 | 5 | Copyright (c) 2004-2013 Ross Bencina 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 22 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | /* 28 | The text above constitutes the entire oscpack license; however, 29 | the oscpack developer(s) also make the following non-binding requests: 30 | 31 | Any person wishing to distribute modifications to the Software is 32 | requested to send the modifications to the original developer so that 33 | they can be incorporated into the canonical version. It is also 34 | requested that these non-binding requests be included whenever the 35 | above license is reproduced. 36 | */ 37 | #include "OscPrintReceivedElements.h" 38 | 39 | #include // Assert on linux 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #if defined(__BORLANDC__) // workaround for BCB4 release build intrinsics bug 46 | namespace std { 47 | using ::__strcpy__; // avoid error: E2316 '__strcpy__' is not a member of 'std'. 48 | } 49 | #endif 50 | 51 | namespace osc{ 52 | 53 | 54 | std::ostream& operator<<( std::ostream & os, 55 | const ReceivedMessageArgument& arg ) 56 | { 57 | switch( arg.TypeTag() ){ 58 | case TRUE_TYPE_TAG: 59 | os << "bool:true"; 60 | break; 61 | 62 | case FALSE_TYPE_TAG: 63 | os << "bool:false"; 64 | break; 65 | 66 | case NIL_TYPE_TAG: 67 | os << "(Nil)"; 68 | break; 69 | 70 | case INFINITUM_TYPE_TAG: 71 | os << "(Infinitum)"; 72 | break; 73 | 74 | case INT32_TYPE_TAG: 75 | os << "int32:" << arg.AsInt32Unchecked(); 76 | break; 77 | 78 | case FLOAT_TYPE_TAG: 79 | os << "float32:" << arg.AsFloatUnchecked(); 80 | break; 81 | 82 | case CHAR_TYPE_TAG: 83 | { 84 | char s[2] = {0}; 85 | s[0] = arg.AsCharUnchecked(); 86 | os << "char:'" << s << "'"; 87 | } 88 | break; 89 | 90 | case RGBA_COLOR_TYPE_TAG: 91 | { 92 | uint32 color = arg.AsRgbaColorUnchecked(); 93 | 94 | os << "RGBA:0x" 95 | << std::hex << std::setfill('0') 96 | << std::setw(2) << (int)((color>>24) & 0xFF) 97 | << std::setw(2) << (int)((color>>16) & 0xFF) 98 | << std::setw(2) << (int)((color>>8) & 0xFF) 99 | << std::setw(2) << (int)(color & 0xFF) 100 | << std::setfill(' '); 101 | os.unsetf(std::ios::basefield); 102 | } 103 | break; 104 | 105 | case MIDI_MESSAGE_TYPE_TAG: 106 | { 107 | uint32 m = arg.AsMidiMessageUnchecked(); 108 | os << "midi (port, status, data1, data2):<<" 109 | << std::hex << std::setfill('0') 110 | << "0x" << std::setw(2) << (int)((m>>24) & 0xFF) 111 | << " 0x" << std::setw(2) << (int)((m>>16) & 0xFF) 112 | << " 0x" << std::setw(2) << (int)((m>>8) & 0xFF) 113 | << " 0x" << std::setw(2) << (int)(m & 0xFF) 114 | << std::setfill(' ') << ">>"; 115 | os.unsetf(std::ios::basefield); 116 | } 117 | break; 118 | 119 | case INT64_TYPE_TAG: 120 | os << "int64:" << arg.AsInt64Unchecked(); 121 | break; 122 | 123 | case TIME_TAG_TYPE_TAG: 124 | { 125 | os << "OSC-timetag:" << arg.AsTimeTagUnchecked() << " "; 126 | 127 | std::time_t t = 128 | (unsigned long)( arg.AsTimeTagUnchecked() >> 32 ); 129 | 130 | const char *timeString = std::ctime( &t ); 131 | size_t len = std::strlen( timeString ); 132 | 133 | // -1 to omit trailing newline from string returned by ctime() 134 | if( len > 1 ) 135 | os.write( timeString, len - 1 ); 136 | } 137 | break; 138 | 139 | case DOUBLE_TYPE_TAG: 140 | os << "double:" << arg.AsDoubleUnchecked(); 141 | break; 142 | 143 | case STRING_TYPE_TAG: 144 | os << "OSC-string:`" << arg.AsStringUnchecked() << "'"; 145 | break; 146 | 147 | case SYMBOL_TYPE_TAG: 148 | os << "OSC-string (symbol):`" << arg.AsSymbolUnchecked() << "'"; 149 | break; 150 | 151 | case BLOB_TYPE_TAG: 152 | { 153 | const void *data; 154 | osc_bundle_element_size_t size; 155 | arg.AsBlobUnchecked( data, size ); 156 | os << "OSC-blob:<<" << std::hex << std::setfill('0'); 157 | unsigned char *p = (unsigned char*)data; 158 | for( osc_bundle_element_size_t i = 0; i < size; ++i ){ 159 | os << "0x" << std::setw(2) << int(p[i]); 160 | if( i != size-1 ) 161 | os << ' '; 162 | } 163 | os.unsetf(std::ios::basefield); 164 | os << ">>" << std::setfill(' '); 165 | } 166 | break; 167 | 168 | case ARRAY_BEGIN_TYPE_TAG: 169 | os << "["; 170 | break; 171 | 172 | case ARRAY_END_TYPE_TAG: 173 | os << "]"; 174 | break; 175 | 176 | default: 177 | os << "unknown"; 178 | } 179 | 180 | return os; 181 | } 182 | 183 | 184 | std::ostream& operator<<( std::ostream & os, const ReceivedMessage& m ) 185 | { 186 | os << "["; 187 | if( m.AddressPatternIsUInt32() ) 188 | os << m.AddressPatternAsUInt32(); 189 | else 190 | os << m.AddressPattern(); 191 | 192 | bool first = true; 193 | for( ReceivedMessage::const_iterator i = m.ArgumentsBegin(); 194 | i != m.ArgumentsEnd(); ++i ){ 195 | if( first ){ 196 | os << " "; 197 | first = false; 198 | }else{ 199 | os << ", "; 200 | } 201 | 202 | os << *i; 203 | } 204 | 205 | os << "]"; 206 | 207 | return os; 208 | } 209 | 210 | 211 | std::ostream& operator<<( std::ostream & os, const ReceivedBundle& b ) 212 | { 213 | static int indent = 0; 214 | 215 | for( int j=0; j < indent; ++j ) 216 | os << " "; 217 | os << "{ ( "; 218 | if( b.TimeTag() == 1 ) 219 | os << "immediate"; 220 | else 221 | os << b.TimeTag(); 222 | os << " )\n"; 223 | 224 | ++indent; 225 | 226 | for( ReceivedBundle::const_iterator i = b.ElementsBegin(); 227 | i != b.ElementsEnd(); ++i ){ 228 | if( i->IsBundle() ){ 229 | ReceivedBundle b(*i); 230 | os << b << "\n"; 231 | }else{ 232 | ReceivedMessage m(*i); 233 | for( int j=0; j < indent; ++j ) 234 | os << " "; 235 | os << m << "\n"; 236 | } 237 | } 238 | 239 | --indent; 240 | 241 | for( int j=0; j < indent; ++j ) 242 | os << " "; 243 | os << "}"; 244 | 245 | return os; 246 | } 247 | 248 | 249 | std::ostream& operator<<( std::ostream & os, const ReceivedPacket& p ) 250 | { 251 | if( p.IsBundle() ){ 252 | ReceivedBundle b(p); 253 | os << b << "\n"; 254 | }else{ 255 | ReceivedMessage m(p); 256 | os << m << "\n"; 257 | } 258 | 259 | return os; 260 | } 261 | 262 | } // namespace osc 263 | -------------------------------------------------------------------------------- /src/osc/oscpack/osc/OscPrintReceivedElements.h: -------------------------------------------------------------------------------- 1 | /* 2 | oscpack -- Open Sound Control (OSC) packet manipulation library 3 | http://www.rossbencina.com/code/oscpack 4 | 5 | Copyright (c) 2004-2013 Ross Bencina 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 22 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | /* 28 | The text above constitutes the entire oscpack license; however, 29 | the oscpack developer(s) also make the following non-binding requests: 30 | 31 | Any person wishing to distribute modifications to the Software is 32 | requested to send the modifications to the original developer so that 33 | they can be incorporated into the canonical version. It is also 34 | requested that these non-binding requests be included whenever the 35 | above license is reproduced. 36 | */ 37 | #ifndef INCLUDED_OSCPACK_OSCPRINTRECEIVEDELEMENTS_H 38 | #define INCLUDED_OSCPACK_OSCPRINTRECEIVEDELEMENTS_H 39 | 40 | #include 41 | 42 | #include "OscReceivedElements.h" 43 | 44 | 45 | namespace osc{ 46 | 47 | std::ostream& operator<<( std::ostream & os, const ReceivedPacket& p ); 48 | std::ostream& operator<<( std::ostream & os, const ReceivedMessageArgument& arg ); 49 | std::ostream& operator<<( std::ostream & os, const ReceivedMessage& m ); 50 | std::ostream& operator<<( std::ostream & os, const ReceivedBundle& b ); 51 | 52 | } // namespace osc 53 | 54 | #endif /* INCLUDED_OSCPACK_OSCPRINTRECEIVEDELEMENTS_H */ 55 | -------------------------------------------------------------------------------- /src/osc/oscpack/osc/OscReceivedElements.h: -------------------------------------------------------------------------------- 1 | /* 2 | oscpack -- Open Sound Control (OSC) packet manipulation library 3 | http://www.rossbencina.com/code/oscpack 4 | 5 | Copyright (c) 2004-2013 Ross Bencina 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 22 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | /* 28 | The text above constitutes the entire oscpack license; however, 29 | the oscpack developer(s) also make the following non-binding requests: 30 | 31 | Any person wishing to distribute modifications to the Software is 32 | requested to send the modifications to the original developer so that 33 | they can be incorporated into the canonical version. It is also 34 | requested that these non-binding requests be included whenever the 35 | above license is reproduced. 36 | */ 37 | #ifndef INCLUDED_OSCPACK_OSCRECEIVEDELEMENTS_H 38 | #define INCLUDED_OSCPACK_OSCRECEIVEDELEMENTS_H 39 | 40 | #include 41 | #include 42 | #include // size_t 43 | 44 | #include "OscTypes.h" 45 | #include "OscException.h" 46 | 47 | 48 | namespace osc{ 49 | 50 | 51 | class MalformedPacketException : public Exception{ 52 | public: 53 | MalformedPacketException( const char *w="malformed packet" ) 54 | : Exception( w ) {} 55 | }; 56 | 57 | class MalformedMessageException : public Exception{ 58 | public: 59 | MalformedMessageException( const char *w="malformed message" ) 60 | : Exception( w ) {} 61 | }; 62 | 63 | class MalformedBundleException : public Exception{ 64 | public: 65 | MalformedBundleException( const char *w="malformed bundle" ) 66 | : Exception( w ) {} 67 | }; 68 | 69 | class WrongArgumentTypeException : public Exception{ 70 | public: 71 | WrongArgumentTypeException( const char *w="wrong argument type" ) 72 | : Exception( w ) {} 73 | }; 74 | 75 | class MissingArgumentException : public Exception{ 76 | public: 77 | MissingArgumentException( const char *w="missing argument" ) 78 | : Exception( w ) {} 79 | }; 80 | 81 | class ExcessArgumentException : public Exception{ 82 | public: 83 | ExcessArgumentException( const char *w="too many arguments" ) 84 | : Exception( w ) {} 85 | }; 86 | 87 | 88 | class ReceivedPacket{ 89 | public: 90 | // Although the OSC spec is not entirely clear on this, we only support 91 | // packets up to 0x7FFFFFFC bytes long (the maximum 4-byte aligned value 92 | // representable by an int32). An exception will be raised if you pass a 93 | // larger value to the ReceivedPacket() constructor. 94 | 95 | ReceivedPacket( const char *contents, osc_bundle_element_size_t size ) 96 | : contents_( contents ) 97 | , size_( ValidateSize(size) ) {} 98 | 99 | ReceivedPacket( const char *contents, std::size_t size ) 100 | : contents_( contents ) 101 | , size_( ValidateSize( (osc_bundle_element_size_t)size ) ) {} 102 | 103 | #if !(defined(__x86_64__) || defined(_M_X64)) 104 | ReceivedPacket( const char *contents, int size ) 105 | : contents_( contents ) 106 | , size_( ValidateSize( (osc_bundle_element_size_t)size ) ) {} 107 | #endif 108 | 109 | bool IsMessage() const { return !IsBundle(); } 110 | bool IsBundle() const; 111 | 112 | osc_bundle_element_size_t Size() const { return size_; } 113 | const char *Contents() const { return contents_; } 114 | 115 | private: 116 | const char *contents_; 117 | osc_bundle_element_size_t size_; 118 | 119 | static osc_bundle_element_size_t ValidateSize( osc_bundle_element_size_t size ) 120 | { 121 | // sanity check integer types declared in OscTypes.h 122 | // you'll need to fix OscTypes.h if any of these asserts fail 123 | assert( sizeof(osc::int32) == 4 ); 124 | assert( sizeof(osc::uint32) == 4 ); 125 | assert( sizeof(osc::int64) == 8 ); 126 | assert( sizeof(osc::uint64) == 8 ); 127 | 128 | if( !IsValidElementSizeValue(size) ) 129 | throw MalformedPacketException( "invalid packet size" ); 130 | 131 | if( size == 0 ) 132 | throw MalformedPacketException( "zero length elements not permitted" ); 133 | 134 | if( !IsMultipleOf4(size) ) 135 | throw MalformedPacketException( "element size must be multiple of four" ); 136 | 137 | return size; 138 | } 139 | }; 140 | 141 | 142 | class ReceivedBundleElement{ 143 | public: 144 | ReceivedBundleElement( const char *sizePtr ) 145 | : sizePtr_( sizePtr ) {} 146 | 147 | friend class ReceivedBundleElementIterator; 148 | 149 | bool IsMessage() const { return !IsBundle(); } 150 | bool IsBundle() const; 151 | 152 | osc_bundle_element_size_t Size() const; 153 | const char *Contents() const { return sizePtr_ + osc::OSC_SIZEOF_INT32; } 154 | 155 | private: 156 | const char *sizePtr_; 157 | }; 158 | 159 | 160 | class ReceivedBundleElementIterator{ 161 | public: 162 | ReceivedBundleElementIterator( const char *sizePtr ) 163 | : value_( sizePtr ) {} 164 | 165 | ReceivedBundleElementIterator operator++() 166 | { 167 | Advance(); 168 | return *this; 169 | } 170 | 171 | ReceivedBundleElementIterator operator++(int) 172 | { 173 | ReceivedBundleElementIterator old( *this ); 174 | Advance(); 175 | return old; 176 | } 177 | 178 | const ReceivedBundleElement& operator*() const { return value_; } 179 | 180 | const ReceivedBundleElement* operator->() const { return &value_; } 181 | 182 | friend bool operator==(const ReceivedBundleElementIterator& lhs, 183 | const ReceivedBundleElementIterator& rhs ); 184 | 185 | private: 186 | ReceivedBundleElement value_; 187 | 188 | void Advance() { value_.sizePtr_ = value_.Contents() + value_.Size(); } 189 | 190 | bool IsEqualTo( const ReceivedBundleElementIterator& rhs ) const 191 | { 192 | return value_.sizePtr_ == rhs.value_.sizePtr_; 193 | } 194 | }; 195 | 196 | inline bool operator==(const ReceivedBundleElementIterator& lhs, 197 | const ReceivedBundleElementIterator& rhs ) 198 | { 199 | return lhs.IsEqualTo( rhs ); 200 | } 201 | 202 | inline bool operator!=(const ReceivedBundleElementIterator& lhs, 203 | const ReceivedBundleElementIterator& rhs ) 204 | { 205 | return !( lhs == rhs ); 206 | } 207 | 208 | 209 | class ReceivedMessageArgument{ 210 | public: 211 | ReceivedMessageArgument( const char *typeTagPtr, const char *argumentPtr ) 212 | : typeTagPtr_( typeTagPtr ) 213 | , argumentPtr_( argumentPtr ) {} 214 | 215 | friend class ReceivedMessageArgumentIterator; 216 | 217 | char TypeTag() const { return *typeTagPtr_; } 218 | 219 | // the unchecked methods below don't check whether the argument actually 220 | // is of the specified type. they should only be used if you've already 221 | // checked the type tag or the associated IsType() method. 222 | 223 | bool IsBool() const 224 | { return *typeTagPtr_ == TRUE_TYPE_TAG || *typeTagPtr_ == FALSE_TYPE_TAG; } 225 | bool AsBool() const; 226 | bool AsBoolUnchecked() const; 227 | 228 | bool IsNil() const { return *typeTagPtr_ == NIL_TYPE_TAG; } 229 | bool IsInfinitum() const { return *typeTagPtr_ == INFINITUM_TYPE_TAG; } 230 | 231 | bool IsInt32() const { return *typeTagPtr_ == INT32_TYPE_TAG; } 232 | int32 AsInt32() const; 233 | int32 AsInt32Unchecked() const; 234 | 235 | bool IsFloat() const { return *typeTagPtr_ == FLOAT_TYPE_TAG; } 236 | float AsFloat() const; 237 | float AsFloatUnchecked() const; 238 | 239 | bool IsChar() const { return *typeTagPtr_ == CHAR_TYPE_TAG; } 240 | char AsChar() const; 241 | char AsCharUnchecked() const; 242 | 243 | bool IsRgbaColor() const { return *typeTagPtr_ == RGBA_COLOR_TYPE_TAG; } 244 | uint32 AsRgbaColor() const; 245 | uint32 AsRgbaColorUnchecked() const; 246 | 247 | bool IsMidiMessage() const { return *typeTagPtr_ == MIDI_MESSAGE_TYPE_TAG; } 248 | uint32 AsMidiMessage() const; 249 | uint32 AsMidiMessageUnchecked() const; 250 | 251 | bool IsInt64() const { return *typeTagPtr_ == INT64_TYPE_TAG; } 252 | int64 AsInt64() const; 253 | int64 AsInt64Unchecked() const; 254 | 255 | bool IsTimeTag() const { return *typeTagPtr_ == TIME_TAG_TYPE_TAG; } 256 | uint64 AsTimeTag() const; 257 | uint64 AsTimeTagUnchecked() const; 258 | 259 | bool IsDouble() const { return *typeTagPtr_ == DOUBLE_TYPE_TAG; } 260 | double AsDouble() const; 261 | double AsDoubleUnchecked() const; 262 | 263 | bool IsString() const { return *typeTagPtr_ == STRING_TYPE_TAG; } 264 | const char* AsString() const; 265 | const char* AsStringUnchecked() const { return argumentPtr_; } 266 | 267 | bool IsSymbol() const { return *typeTagPtr_ == SYMBOL_TYPE_TAG; } 268 | const char* AsSymbol() const; 269 | const char* AsSymbolUnchecked() const { return argumentPtr_; } 270 | 271 | bool IsBlob() const { return *typeTagPtr_ == BLOB_TYPE_TAG; } 272 | void AsBlob( const void*& data, osc_bundle_element_size_t& size ) const; 273 | void AsBlobUnchecked( const void*& data, osc_bundle_element_size_t& size ) const; 274 | 275 | bool IsArrayBegin() const { return *typeTagPtr_ == ARRAY_BEGIN_TYPE_TAG; } 276 | bool IsArrayEnd() const { return *typeTagPtr_ == ARRAY_END_TYPE_TAG; } 277 | // Calculate the number of top-level items in the array. Nested arrays count as one item. 278 | // Only valid at array start. Will throw an exception if IsArrayStart() == false. 279 | std::size_t ComputeArrayItemCount() const; 280 | 281 | private: 282 | const char *typeTagPtr_; 283 | const char *argumentPtr_; 284 | }; 285 | 286 | 287 | class ReceivedMessageArgumentIterator{ 288 | public: 289 | ReceivedMessageArgumentIterator( const char *typeTags, const char *arguments ) 290 | : value_( typeTags, arguments ) {} 291 | 292 | ReceivedMessageArgumentIterator operator++() 293 | { 294 | Advance(); 295 | return *this; 296 | } 297 | 298 | ReceivedMessageArgumentIterator operator++(int) 299 | { 300 | ReceivedMessageArgumentIterator old( *this ); 301 | Advance(); 302 | return old; 303 | } 304 | 305 | const ReceivedMessageArgument& operator*() const { return value_; } 306 | 307 | const ReceivedMessageArgument* operator->() const { return &value_; } 308 | 309 | friend bool operator==(const ReceivedMessageArgumentIterator& lhs, 310 | const ReceivedMessageArgumentIterator& rhs ); 311 | 312 | private: 313 | ReceivedMessageArgument value_; 314 | 315 | void Advance(); 316 | 317 | bool IsEqualTo( const ReceivedMessageArgumentIterator& rhs ) const 318 | { 319 | return value_.typeTagPtr_ == rhs.value_.typeTagPtr_; 320 | } 321 | }; 322 | 323 | inline bool operator==(const ReceivedMessageArgumentIterator& lhs, 324 | const ReceivedMessageArgumentIterator& rhs ) 325 | { 326 | return lhs.IsEqualTo( rhs ); 327 | } 328 | 329 | inline bool operator!=(const ReceivedMessageArgumentIterator& lhs, 330 | const ReceivedMessageArgumentIterator& rhs ) 331 | { 332 | return !( lhs == rhs ); 333 | } 334 | 335 | 336 | class ReceivedMessageArgumentStream{ 337 | friend class ReceivedMessage; 338 | ReceivedMessageArgumentStream( const ReceivedMessageArgumentIterator& begin, 339 | const ReceivedMessageArgumentIterator& end ) 340 | : p_( begin ) 341 | , end_( end ) {} 342 | 343 | ReceivedMessageArgumentIterator p_, end_; 344 | 345 | public: 346 | 347 | // end of stream 348 | bool Eos() const { return p_ == end_; } 349 | 350 | ReceivedMessageArgumentStream& operator>>( bool& rhs ) 351 | { 352 | if( Eos() ) 353 | throw MissingArgumentException(); 354 | 355 | rhs = (*p_++).AsBool(); 356 | return *this; 357 | } 358 | 359 | // not sure if it would be useful to stream Nil and Infinitum 360 | // for now it's not possible 361 | // same goes for array boundaries 362 | 363 | ReceivedMessageArgumentStream& operator>>( int32& rhs ) 364 | { 365 | if( Eos() ) 366 | throw MissingArgumentException(); 367 | 368 | rhs = (*p_++).AsInt32(); 369 | return *this; 370 | } 371 | 372 | ReceivedMessageArgumentStream& operator>>( float& rhs ) 373 | { 374 | if( Eos() ) 375 | throw MissingArgumentException(); 376 | 377 | rhs = (*p_++).AsFloat(); 378 | return *this; 379 | } 380 | 381 | ReceivedMessageArgumentStream& operator>>( char& rhs ) 382 | { 383 | if( Eos() ) 384 | throw MissingArgumentException(); 385 | 386 | rhs = (*p_++).AsChar(); 387 | return *this; 388 | } 389 | 390 | ReceivedMessageArgumentStream& operator>>( RgbaColor& rhs ) 391 | { 392 | if( Eos() ) 393 | throw MissingArgumentException(); 394 | 395 | rhs.value = (*p_++).AsRgbaColor(); 396 | return *this; 397 | } 398 | 399 | ReceivedMessageArgumentStream& operator>>( MidiMessage& rhs ) 400 | { 401 | if( Eos() ) 402 | throw MissingArgumentException(); 403 | 404 | rhs.value = (*p_++).AsMidiMessage(); 405 | return *this; 406 | } 407 | 408 | ReceivedMessageArgumentStream& operator>>( int64& rhs ) 409 | { 410 | if( Eos() ) 411 | throw MissingArgumentException(); 412 | 413 | rhs = (*p_++).AsInt64(); 414 | return *this; 415 | } 416 | 417 | ReceivedMessageArgumentStream& operator>>( TimeTag& rhs ) 418 | { 419 | if( Eos() ) 420 | throw MissingArgumentException(); 421 | 422 | rhs.value = (*p_++).AsTimeTag(); 423 | return *this; 424 | } 425 | 426 | ReceivedMessageArgumentStream& operator>>( double& rhs ) 427 | { 428 | if( Eos() ) 429 | throw MissingArgumentException(); 430 | 431 | rhs = (*p_++).AsDouble(); 432 | return *this; 433 | } 434 | 435 | ReceivedMessageArgumentStream& operator>>( Blob& rhs ) 436 | { 437 | if( Eos() ) 438 | throw MissingArgumentException(); 439 | 440 | (*p_++).AsBlob( rhs.data, rhs.size ); 441 | return *this; 442 | } 443 | 444 | ReceivedMessageArgumentStream& operator>>( const char*& rhs ) 445 | { 446 | if( Eos() ) 447 | throw MissingArgumentException(); 448 | 449 | rhs = (*p_++).AsString(); 450 | return *this; 451 | } 452 | 453 | ReceivedMessageArgumentStream& operator>>( Symbol& rhs ) 454 | { 455 | if( Eos() ) 456 | throw MissingArgumentException(); 457 | 458 | rhs.value = (*p_++).AsSymbol(); 459 | return *this; 460 | } 461 | 462 | ReceivedMessageArgumentStream& operator>>( MessageTerminator& rhs ) 463 | { 464 | (void) rhs; // suppress unused parameter warning 465 | 466 | if( !Eos() ) 467 | throw ExcessArgumentException(); 468 | 469 | return *this; 470 | } 471 | }; 472 | 473 | 474 | class ReceivedMessage{ 475 | void Init( const char *bundle, osc_bundle_element_size_t size ); 476 | public: 477 | explicit ReceivedMessage( const ReceivedPacket& packet ); 478 | explicit ReceivedMessage( const ReceivedBundleElement& bundleElement ); 479 | 480 | const char *AddressPattern() const { return addressPattern_; } 481 | 482 | // Support for non-standard SuperCollider integer address patterns: 483 | bool AddressPatternIsUInt32() const; 484 | uint32 AddressPatternAsUInt32() const; 485 | 486 | uint32 ArgumentCount() const { return static_cast(typeTagsEnd_ - typeTagsBegin_); } 487 | 488 | const char *TypeTags() const { return typeTagsBegin_; } 489 | 490 | 491 | typedef ReceivedMessageArgumentIterator const_iterator; 492 | 493 | ReceivedMessageArgumentIterator ArgumentsBegin() const 494 | { 495 | return ReceivedMessageArgumentIterator( typeTagsBegin_, arguments_ ); 496 | } 497 | 498 | ReceivedMessageArgumentIterator ArgumentsEnd() const 499 | { 500 | return ReceivedMessageArgumentIterator( typeTagsEnd_, 0 ); 501 | } 502 | 503 | ReceivedMessageArgumentStream ArgumentStream() const 504 | { 505 | return ReceivedMessageArgumentStream( ArgumentsBegin(), ArgumentsEnd() ); 506 | } 507 | 508 | private: 509 | const char *addressPattern_; 510 | const char *typeTagsBegin_; 511 | const char *typeTagsEnd_; 512 | const char *arguments_; 513 | }; 514 | 515 | 516 | class ReceivedBundle{ 517 | void Init( const char *message, osc_bundle_element_size_t size ); 518 | public: 519 | explicit ReceivedBundle( const ReceivedPacket& packet ); 520 | explicit ReceivedBundle( const ReceivedBundleElement& bundleElement ); 521 | 522 | uint64 TimeTag() const; 523 | 524 | uint32 ElementCount() const { return elementCount_; } 525 | 526 | typedef ReceivedBundleElementIterator const_iterator; 527 | 528 | ReceivedBundleElementIterator ElementsBegin() const 529 | { 530 | return ReceivedBundleElementIterator( timeTag_ + 8 ); 531 | } 532 | 533 | ReceivedBundleElementIterator ElementsEnd() const 534 | { 535 | return ReceivedBundleElementIterator( end_ ); 536 | } 537 | 538 | private: 539 | const char *timeTag_; 540 | const char *end_; 541 | uint32 elementCount_; 542 | }; 543 | 544 | 545 | } // namespace osc 546 | 547 | 548 | #endif /* INCLUDED_OSCPACK_OSCRECEIVEDELEMENTS_H */ 549 | -------------------------------------------------------------------------------- /src/osc/oscpack/osc/OscTypes.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | oscpack -- Open Sound Control (OSC) packet manipulation library 3 | http://www.rossbencina.com/code/oscpack 4 | 5 | Copyright (c) 2004-2013 Ross Bencina 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 22 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | /* 28 | The text above constitutes the entire oscpack license; however, 29 | the oscpack developer(s) also make the following non-binding requests: 30 | 31 | Any person wishing to distribute modifications to the Software is 32 | requested to send the modifications to the original developer so that 33 | they can be incorporated into the canonical version. It is also 34 | requested that these non-binding requests be included whenever the 35 | above license is reproduced. 36 | */ 37 | #include "OscTypes.h" 38 | 39 | namespace osc{ 40 | 41 | BundleInitiator BeginBundleImmediate(1); 42 | BundleTerminator EndBundle; 43 | MessageTerminator EndMessage; 44 | NilType OscNil; 45 | #ifndef _OBJC_OBJC_H_ 46 | NilType Nil; // Objective-C defines Nil. so our Nil is deprecated. use OscNil instead 47 | #endif 48 | InfinitumType Infinitum; 49 | ArrayInitiator BeginArray; 50 | ArrayTerminator EndArray; 51 | 52 | } // namespace osc 53 | -------------------------------------------------------------------------------- /src/osc/oscpack/osc/OscTypes.h: -------------------------------------------------------------------------------- 1 | /* 2 | oscpack -- Open Sound Control (OSC) packet manipulation library 3 | http://www.rossbencina.com/code/oscpack 4 | 5 | Copyright (c) 2004-2013 Ross Bencina 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files 9 | (the "Software"), to deal in the Software without restriction, 10 | including without limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of the Software, 12 | and to permit persons to whom the Software is furnished to do so, 13 | subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 22 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | /* 28 | The text above constitutes the entire oscpack license; however, 29 | the oscpack developer(s) also make the following non-binding requests: 30 | 31 | Any person wishing to distribute modifications to the Software is 32 | requested to send the modifications to the original developer so that 33 | they can be incorporated into the canonical version. It is also 34 | requested that these non-binding requests be included whenever the 35 | above license is reproduced. 36 | */ 37 | #ifndef INCLUDED_OSCPACK_OSCTYPES_H 38 | #define INCLUDED_OSCPACK_OSCTYPES_H 39 | 40 | #include 41 | 42 | namespace osc 43 | { 44 | 45 | // basic types 46 | 47 | #if defined(__BORLANDC__) || defined(_MSC_VER) 48 | 49 | typedef __int64 int64; 50 | typedef unsigned __int64 uint64; 51 | 52 | #elif defined(__x86_64__) || defined(_M_X64) 53 | 54 | typedef int64_t int64; 55 | typedef uint64_t uint64; 56 | 57 | #else 58 | 59 | typedef long long int64; 60 | typedef unsigned long long uint64; 61 | 62 | #endif 63 | 64 | #if defined(__x86_64__) || defined(_M_X64) 65 | 66 | typedef signed int int32; 67 | typedef unsigned int uint32; 68 | 69 | #else 70 | 71 | typedef signed long int32; 72 | typedef unsigned long uint32; 73 | 74 | #endif 75 | 76 | enum ValueTypeSizes 77 | { 78 | OSC_SIZEOF_INT32 = 4, 79 | OSC_SIZEOF_UINT32 = 4, 80 | OSC_SIZEOF_INT64 = 8, 81 | OSC_SIZEOF_UINT64 = 8, 82 | }; 83 | 84 | // osc_bundle_element_size_t is used for the size of bundle elements and blobs 85 | // the OSC spec specifies these as int32 (signed) but we ensure that they 86 | // are always positive since negative field sizes make no sense. 87 | 88 | typedef int32 osc_bundle_element_size_t; 89 | 90 | enum 91 | { 92 | OSC_INT32_MAX = 0x7FFFFFFF, 93 | 94 | // Element sizes are specified to be int32, and are always rounded up to nearest 95 | // multiple of 4. Therefore their values can't be greater than 0x7FFFFFFC. 96 | OSC_BUNDLE_ELEMENT_SIZE_MAX = 0x7FFFFFFC 97 | }; 98 | 99 | inline bool IsValidElementSizeValue(osc_bundle_element_size_t x) 100 | { 101 | // sizes may not be negative or exceed OSC_BUNDLE_ELEMENT_SIZE_MAX 102 | return x >= 0 && x <= OSC_BUNDLE_ELEMENT_SIZE_MAX; 103 | } 104 | 105 | inline bool IsMultipleOf4(osc_bundle_element_size_t x) 106 | { 107 | return (x & ((osc_bundle_element_size_t)0x03)) == 0; 108 | } 109 | 110 | enum TypeTagValues 111 | { 112 | TRUE_TYPE_TAG = 'T', 113 | FALSE_TYPE_TAG = 'F', 114 | NIL_TYPE_TAG = 'N', 115 | INFINITUM_TYPE_TAG = 'I', 116 | INT32_TYPE_TAG = 'i', 117 | FLOAT_TYPE_TAG = 'f', 118 | CHAR_TYPE_TAG = 'c', 119 | RGBA_COLOR_TYPE_TAG = 'r', 120 | MIDI_MESSAGE_TYPE_TAG = 'm', 121 | INT64_TYPE_TAG = 'h', 122 | TIME_TAG_TYPE_TAG = 't', 123 | DOUBLE_TYPE_TAG = 'd', 124 | STRING_TYPE_TAG = 's', 125 | SYMBOL_TYPE_TAG = 'S', 126 | BLOB_TYPE_TAG = 'b', 127 | ARRAY_BEGIN_TYPE_TAG = '[', 128 | ARRAY_END_TYPE_TAG = ']' 129 | }; 130 | 131 | // i/o manipulators used for streaming interfaces 132 | 133 | struct BundleInitiator 134 | { 135 | explicit BundleInitiator(uint64 timeTag_) 136 | : timeTag(timeTag_) 137 | { 138 | } 139 | uint64 timeTag; 140 | }; 141 | 142 | extern BundleInitiator BeginBundleImmediate; 143 | 144 | inline BundleInitiator BeginBundle(uint64 timeTag = 1) 145 | { 146 | return BundleInitiator(timeTag); 147 | } 148 | 149 | struct BundleTerminator 150 | { 151 | }; 152 | 153 | extern BundleTerminator EndBundle; 154 | 155 | struct BeginMessage 156 | { 157 | explicit BeginMessage(const char* addressPattern_) 158 | : addressPattern(addressPattern_) 159 | { 160 | } 161 | const char* addressPattern; 162 | }; 163 | 164 | struct MessageTerminator 165 | { 166 | }; 167 | 168 | extern MessageTerminator EndMessage; 169 | 170 | // osc specific types. they are defined as structs so they can be used 171 | // as separately identifiable types with the streaming operators. 172 | 173 | struct NilType 174 | { 175 | }; 176 | 177 | extern NilType OscNil; 178 | 179 | #ifndef _OBJC_OBJC_H_ 180 | extern NilType Nil; // Objective-C defines Nil. so our Nil is deprecated. use OscNil instead 181 | #endif 182 | 183 | struct InfinitumType 184 | { 185 | }; 186 | 187 | extern InfinitumType Infinitum; 188 | 189 | struct RgbaColor 190 | { 191 | RgbaColor() {} 192 | explicit RgbaColor(uint32 value_) 193 | : value(value_) 194 | { 195 | } 196 | uint32 value; 197 | 198 | operator uint32() const { return value; } 199 | }; 200 | 201 | struct MidiMessage 202 | { 203 | MidiMessage() {} 204 | explicit MidiMessage(uint32 value_) 205 | : value(value_) 206 | { 207 | } 208 | uint32 value; 209 | 210 | operator uint32() const { return value; } 211 | }; 212 | 213 | struct TimeTag 214 | { 215 | TimeTag() {} 216 | explicit TimeTag(uint64 value_) 217 | : value(value_) 218 | { 219 | } 220 | uint64 value; 221 | 222 | operator uint64() const { return value; } 223 | }; 224 | 225 | struct Symbol 226 | { 227 | Symbol() {} 228 | explicit Symbol(const char* value_) 229 | : value(value_) 230 | { 231 | } 232 | const char* value; 233 | 234 | operator const char*() const { return value; } 235 | }; 236 | 237 | struct Blob 238 | { 239 | Blob() {} 240 | explicit Blob(const void* data_, osc_bundle_element_size_t size_) 241 | : data(data_) 242 | , size(size_) 243 | { 244 | } 245 | const void* data; 246 | osc_bundle_element_size_t size; 247 | }; 248 | 249 | struct ArrayInitiator 250 | { 251 | }; 252 | 253 | extern ArrayInitiator BeginArray; 254 | 255 | struct ArrayTerminator 256 | { 257 | }; 258 | 259 | extern ArrayTerminator EndArray; 260 | 261 | } // namespace osc 262 | 263 | #endif /* INCLUDED_OSCPACK_OSCTYPES_H */ 264 | -------------------------------------------------------------------------------- /src/plugin.cpp: -------------------------------------------------------------------------------- 1 | #include "plugin.hpp" 2 | 3 | Plugin* pluginInstance; 4 | 5 | void init(rack::Plugin* p) { 6 | pluginInstance = p; 7 | p->addModel(modelOSCelot); 8 | p->addModel(modelOscelotExpander); 9 | } -------------------------------------------------------------------------------- /src/plugin.hpp: -------------------------------------------------------------------------------- 1 | #include "rack.hpp" 2 | #include "components.hpp" 3 | #include "helpers.hpp" 4 | 5 | using namespace rack; 6 | 7 | 8 | extern Plugin* pluginInstance; 9 | 10 | extern Model* modelOSCelot; 11 | extern Model* modelOscelotExpander; 12 | 13 | -------------------------------------------------------------------------------- /src/ui/ParamWidgetContextExtender.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "plugin.hpp" 3 | 4 | namespace TheModularMind { 5 | 6 | struct ParamWidgetContextExtender { 7 | Widget* lastSelectedWidget; 8 | 9 | struct CenterModuleItem : MenuItem { 10 | ModuleWidget* mw; 11 | void onAction(const event::Action& e) override { 12 | TheModularMind::Rack::ViewportCenter{mw,0.8f}; 13 | } 14 | }; 15 | 16 | void step() { 17 | Widget* w = APP->event->getDraggedWidget(); 18 | if (!w) return; 19 | 20 | // Only handle right button events 21 | if (APP->event->dragButton != GLFW_MOUSE_BUTTON_RIGHT) { 22 | lastSelectedWidget = NULL; 23 | return; 24 | } 25 | 26 | if (w != lastSelectedWidget) { 27 | lastSelectedWidget = w; 28 | 29 | // Was the last touched widget an ParamWidget? 30 | ParamWidget* pw = dynamic_cast(w); 31 | if (!pw) return; 32 | 33 | // Retrieve the context menu, if available 34 | MenuOverlay* overlay = NULL; 35 | for (auto rit = APP->scene->children.rbegin(); rit != APP->scene->children.rend(); rit++) { 36 | overlay = dynamic_cast(*rit); 37 | if (overlay) break; 38 | } 39 | if (!overlay) return; 40 | 41 | Menu* menu = overlay->getFirstDescendantOfType(); 42 | if (!menu) return; 43 | 44 | extendParamWidgetContextMenu(pw, menu); 45 | } 46 | } 47 | 48 | virtual void extendParamWidgetContextMenu(ParamWidget* pw, Menu* menu) { } 49 | 50 | }; // struct ParamWidgetContextExtender 51 | 52 | } // namespace TheModularMind -------------------------------------------------------------------------------- /src/ui/ThemedModuleWidget.hpp: -------------------------------------------------------------------------------- 1 | #include "plugin.hpp" 2 | #include 3 | 4 | namespace TheModularMind { 5 | 6 | template < typename MODULE, typename BASE = ModuleWidget > 7 | struct ThemedModuleWidget : BASE { 8 | MODULE* module; 9 | std::string baseName; 10 | std::string manualName; 11 | int panelTheme = -1; 12 | 13 | struct SplitPanel : SvgPanel { 14 | ThemedModuleWidget* w; 15 | SvgPanel* t; 16 | SvgPanel* u; 17 | void draw(const DrawArgs& args) override { 18 | if (!w) return; 19 | nvgScissor(args.vg, w->box.size.x / 4.f, 0, w->box.size.x / 4.f, w->box.size.y); 20 | SvgPanel::draw(args); 21 | nvgResetScissor(args.vg); 22 | 23 | nvgScissor(args.vg, t->box.size.x * 2.f / 4.f, 0, t->box.size.x * 2.f / 4.f, t->box.size.y); 24 | t->draw(args); 25 | 26 | nvgScissor(args.vg, u->box.size.x * 3.f / 4.f, 0, u->box.size.x * 3.f / 4.f, u->box.size.y); 27 | u->draw(args); 28 | nvgResetScissor(args.vg); 29 | } 30 | }; 31 | 32 | ThemedModuleWidget(MODULE* module, std::string baseName, std::string manualName = "") { 33 | this->module = module; 34 | this->baseName = baseName; 35 | this->manualName = manualName; 36 | 37 | if (module) { 38 | // Normal operation 39 | BASE::setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, panel()))); 40 | } 41 | else { 42 | // Module Browser 43 | BASE::setPanel(Svg::load(asset::plugin(pluginInstance, "res/" + baseName + "_Brass.svg"))); 44 | SplitPanel* splitPanel = new SplitPanel(); 45 | SvgPanel* t = new SvgPanel(); 46 | SvgPanel* u = new SvgPanel(); 47 | t->setBackground(Svg::load(asset::plugin(pluginInstance, "res/" + baseName + "_BlueSteel.svg"))); 48 | u->setBackground(Svg::load(asset::plugin(pluginInstance, "res/" + baseName + "_BlackSteel.svg"))); 49 | splitPanel->t = t; 50 | splitPanel->u = u; 51 | 52 | splitPanel->w = this; 53 | splitPanel->setBackground(Svg::load(asset::plugin(pluginInstance, "res/" + baseName + "_GunMetal.svg"))); 54 | BASE::addChild(splitPanel); 55 | } 56 | } 57 | 58 | void appendContextMenu(Menu* menu) override { 59 | menu->addChild(new MenuSeparator()); 60 | menu->addChild(createIndexPtrSubmenuItem("Panel", {"Gun Metal", "Blue Steel", "Yellow Brass", "Black Steel"}, &module->panelTheme)); 61 | BASE::appendContextMenu(menu); 62 | } 63 | 64 | void step() override { 65 | if (module && module->panelTheme != panelTheme) { 66 | panelTheme = module->panelTheme; 67 | BASE::setPanel(Svg::load(asset::plugin(pluginInstance, panel()))); 68 | } 69 | BASE::step(); 70 | } 71 | 72 | std::string panel() { 73 | switch (panelTheme) { 74 | default: 75 | case 0: 76 | return "res/" + baseName + "_GunMetal.svg"; 77 | case 1: 78 | return "res/" + baseName + "_BlueSteel.svg"; 79 | case 2: 80 | return "res/" + baseName + "_Brass.svg"; 81 | case 3: 82 | return "res/" + baseName + "_BlackSteel.svg"; 83 | } 84 | } 85 | }; 86 | 87 | } // namespace TheModularMind --------------------------------------------------------------------------------