Git Product home page Git Product logo

lebaston100 / miditoobs Goto Github PK

View Code? Open in Web Editor NEW
186.0 21.0 36.0 127 KB

(Pre obs-ws 5!!) A python script to use one or multiple midi input device(s) to control OBS-Studio functions without wasting keyboard hotkeys (including some sort of macro support) from anywhere in the network. "Waiting for a nativ implementation since 2017."

Home Page: https://github.com/lebaston100/MIDItoOBS

License: MIT License

Python 99.97% Batchfile 0.03%
obs-studio obs-websocket midi obs-remote python midi-controller

miditoobs's Introduction

Attention!

As of this commit, miditoobs is officially deprecated and will be/is publicly archived.

miditoobs started in 2017 in a gutefrage.net thread (a german version of quora) with a very basic but functional first commit on Sep 17, 2017. Back then there were basically no tools to do what miditoobs did, this sparked the initial development. Now the codebase (mostly the setup) is fairly outdated and a big patchwork of years of adding stuff. Also the complete lack of any abstraction would make migrating to obs-websocket 5 a complete rewrite of everything, which would also break every single config file out there.

A big thanks to every single person who relied on it over the years and everyone who contributed to the project in any form.

If you are looking for alternatives have a look at the Resources List in the OBS forum. The current version of miditoobs will continue to work as long as the OBS project provides obs-websocket v4 compat versions of the websocket plugin (Which probably is still a few months as of writing this).


Original Readme from here on

Midi OBS what???

This script lets you use one or multiple MIDI controllers (like the Novation Launchpad, Ableton Push, Akai LPD or the Korg nanoPAD to mention a few) to switch scenes, start/stop recording/streaming, control volume/filter settings/gain/opacity/t-bar/delay/transition time and more in obs-studio.

If you want to play it safe, use the latest release. If you want to use the latest features then just clone the master branch.

Requirements

Setup Part 1

  • Install Python 3.x.x (whatever the latest version is)
  • On Windows: Make sure you tick "Add Python 3.x to PATH" in the setup
  • Make sure you also install pip
  • For instructions how to install TinyDB click here
  • For instructions how to install mido and python-rtmidi click here
  • For instructions how to install websocket-client click here or here for 4.9.1-compat
  • For instructions how to install dbj click here

If you want to install all packages in one go, run pip install -r requirements.txt

Setup Part 2 OBS Websocket

  • Download the installer and run it
  • Start OBS, open the "Tools" menu and select "WebSockets Server Settings"
  • Make sure that "Enable WebSockets server" is checked, "Server Port" is 4444, "Enable authentication" is unchecked and "Enable System Tray Alerts" is unchecked (trust me, you don't want that on)

Setup Part 3

  • Download the latest Release or clone it if you want to test the bleeding edge features and bugfixes
  • Connect your MIDI controller
  • Launch OBS Studio
  • Run setup.py
  • If you are on Windows, double click or the "Run Setup.bat"
  • If you are on MacOS, run python3 setup.py from your Terminal in the MIDItoOBS folder (ensuring you have activated whatever Python/venv you installed the packages in)
  • If you run the setup for the first time and have not setup a device yet, it will automatically start the device configuration:
  • You will get a list of available MIDI devices. Type the number you want to select and press Enter
  • You will be asked if you want to ad another device.
  • If you only have a single device choose 2 and press Enter, otherwise select 1 and you will get a list with remaining devices.
  • Now you will be asked to press a button or move a fader on your MIDI controller, do that
  • If your MIDI controller sends control change messages, you will also be asked for the type of the input (fader or button)
  • Select an action from the list and press enter. The names represent the request-type in obs-websocket
  • Depending on the action, you will also be asked for the scene and source name (selecting always works by typing in the number and pressing enter). If no source of that type is available and you are prompted to "select 0--1:" then you know that is no such source available in obs and the script will crash trying to select anything. Just add the required object and restart the setup script in this case. (This is already on the todo list for a further update)

Available for buttons:

  • SetCurrentScene: Switches to the scene
  • SetPreviewScene: Puts a scene into preview when in studio mode
  • TransitionToProgram: Transitions the current preview scene to program
  • SetCurrentTransistion: Sets the transition that is used with SetCurrentScene
  • SetSourceVisibility: Hides or unhides a source
  • ToggleSourceVisibility: Toggles the visibility of a source
  • ToggleMute: Toggles the mute status from a source
  • SetMute: Mutes or unmutes a source
  • StartStopStreaming: Toggles the Streaming
  • StartStreaming: Starts streaming
  • StopStreaming: Stops streaming
  • StartStopRecording: Toggles the Recording
  • StartRecording: Starts recording
  • StopRecording: Stops recording
  • StartStopReplayBuffer: Toggles the replay buffer
  • StartReplayBuffer: Starts the replay buffer
  • StopReplayBuffer: Stops the replay buffer
  • SaveReplayBuffer: Save the replay buffer
  • PauseRecording: Pauses the recording
  • ResumeRecording: Resume the recording that was previously paused
  • SetTransitionDuration: Sets the length of the currently selected transition if supported(fade)(in ms) to a predefined value
  • SetCurrentProfile: Changes to the selected obs profile
  • SetCurrentSceneCollection: Changes to the selected obs scene collection
  • ResetSceneItem: Resets a scene item
  • SetTextGDIPlusText: Sets the text of a GDI text sourc
  • SetBrowserSourceURL: Sets the url of a BrowserSource
  • ReloadBrowserSource: Reloads a BrowserSource
  • TakeSourceScreenshot: Don't be fooled by the name; Takes a screenshot of the selected source or complete scene and saves it inside the MIDItoOBS folder as a png image
  • EnableSourceFilter: Enables a filter that is on a source (Works with "Audio Filters" and Video "Effect Filters")
  • DisableSourceFilter: Disables a filter that is on a source (Works with "Audio Filters" and Video "Effect Filters")
  • ToggleSourceFilter: Toggles the status of a filter on a source for each button press
  • SetAudioMonitor: Sets the audio monitor option on a source
  • EnableStudioMode: Enables Studio Mode
  • DisableStudioMode: Disables Studio Mode
  • ToggleStudioMode: Toggles Studio Mode
  • TriggerHotkeyByName: Triggers an obs event, see the obs-websocket wiki for details
  • TriggerHotkeyBySequence: Triggers an obs event based on the configured keyboard combination, see the obs-websocket wiki for details
  • PlayPauseMedia: Start or Pause Media/VLC Source playback
  • ToggleMediaState: Toggle Media/VLC Source playback
  • RestartMedia: Restart Media/VLC Source playback
  • StopMedia: Stop Media/VLC Source playback
  • NextMedia: Jump to the next playlist item. Only works with the vlc source.
  • PreviousMedia: Jump to the previous playlist item. Only works with the vlc source.

Available for faders/knobs:

  • SetVolume: Sets the volume of a source (unlike other solutions this will actually make the fader move in a visual linear way inside obs(Like a % slider))
  • SetSyncOffset: Sets the sync offset of a source [in ns]
  • SetSourcePosition: Sets the x or y position of a source [in px]
  • SetSourceCrop: Set the crop from any edge (left/right/top/bottom) [in px]
  • SetSourceRotation: Sets the rotation of a source [in degree]
  • SetSourceScale: Sets the scale for x/y OR both of a source (For the scaling 1 = original scale). You can also select around which position the source will be scaled(align).
  • SetTransitionDuration: Sets the length of the currently selected transition if supported (fade) [in ms]
  • SetGainFilter: Sets the volume gain value inside the gain filter of a source (For the scaling -30 to 30 is a valid range you can work in). This will automatically default to the first gain filter found in a source!
  • MoveTbar: This will move the transition T-Bar. Make sure you always completely finish a T-Bar move by going to one end to the other otherwise obs will stay in the "a transition is currently happening"-state. Be careful because the state might go "out of sync" with the physical fader if you use any other tools that move the t-bar.
  • Filter/Chroma Key - Contrast: This controls the "Contrast" value for a "Chroma Key" Filter [-1 - 1]
  • Filter/Chroma Key - Brightness: This controls the "Brightness" value for a "Chroma Key" Filter [-1 - 1]
  • Filter/Chroma Key - Gamma: This controls the "Gamma" value for a "Chroma Key" Filter [-1 - 1]
  • Filter/Chroma Key - Opacity: This controls the "Opacity" value for a "Chroma Key" Filter [0 - 100]
  • Filter/Chroma Key - Spill Reduction: This controls the "Key Color Spill Reduction" value for a "Chroma Key" Filter [0 - 1000]
  • Filter/Chroma Key - Similarity: This controls the "Similarity" value for a "Chroma Key" Filter [0 - 1000]
  • Filter/Luma Key - Luma Max: Opacity: This controls the "Luma Max" value for a "Luma Key" Filter [0 - 1]
  • Filter/Luma Key - Luma Max Smooth: This controls the "Luma Max Smooth" value for a "Luma Key" Filter [0 - 1]
  • Filter/Luma Key - Luma Min: Opacity: This controls the "Luma Min" value for a "Luma Key" Filter [0 - 1]
  • Filter/Luma Key - Luma Min Smooth: This controls the "Luma Min Smooth" value for a "Luma Key" Filter [0 - 1]
  • Filter/Color Correction - Saturation: This controls the "Saturation" value for a "Color Correction" Filter [-1 - 5]
  • Filter/Color Correction - Contrast: This controls the "Contrast" value for a "Color Correction" Filter [-2 - 2]
  • Filter/Color Correction - Brightness: This controls the "Brightness" value for a "Color Correction" Filter [-1 - 1]
  • Filter/Color Correction - Gamma: This controls the "Gamma" value for a "Color Correction" Filter [-3 - 3]
  • Filter/Color Correction - Hue Shift: This controls the "Gamma" value for a "Color Correction" Filter [-180 - 180] (Replaces the old SetColorCorrectionHueShift)
  • Filter/Color Correction - Opacity: This controls the "Opacity" value for a "Color Correction" Filter [0 - 100] (Replaces the old SetOpacity)
  • Filter/Color Key - Similarity: This controls the "Similarity" value for a "Color Key" Filter [1 - 1000]
  • Filter/Color Key - Smoothness: This controls the "Smoothness" value for a "Color Key" Filter [1 - 1000]
  • Filter/Color Key - Brightness: This controls the "Brightness" value for a "Color Key" Filter [-1 - 1]
  • Filter/Color Key - Contrast: This controls the "Contrast" value for a "Color Key" Filter [-1 - 1]
  • Filter/Color Key - Gamma: This controls the "Gamma" value for a "Color Key" Filter [-1 - 1]
  • Filter/Sharpen - Sharpness: This controls the "Sharpness" value for a "Sharpen" Filter [0 - 1]
  • Filter/Scroll - Horizontal Speed: This controls the "Horizontal Speed" value for a "Scroll" Filter [-500 - 500]
  • Filter/Scroll - Vertical Speed: This controls the "Vertical Speed" value for a "Scroll" Filter [-500 - 500]
  • Filter/Video Delay (Async) - Delay: This controls the "Delay" value (in ms) for a "ideo Delay (Async)" Filter [-0 - 20000]
  • Filter/Render Delay - Delay: This controls the "Delay" value (in ms) for a "Render Delay" Filter [0 - 500]
  • Filter/Generic Filter - Generic Setting: This can control every property of any filter, even filters added by plugins or on (global) audio sources. You have to specify what the setting property is called ,either by manually calling GetSourceFilterInfo via obs-websocket or by changing the default value via obs which then shows up in a list in the setup. You also have to specify if the data should be a Int (Whole Number) or Float (Floating Point Number)
  • Now you can either setup another button/fader by repeating the steps above(except starting the script again) or just close the window to exit the configuration

Important note about all controls that involve a scene:

In OBS, scenes are also sources, so all the filter controls and TakeSourceScreenshot will also work on scenes. They will be part of the list that you are prompted with in the setup. For a detailed description of most of the commands see the obs-websocket protocol documentation

Device Management

If you run the setup another time after the initial configuration you will get a dialog at startup where you can select if you want to go to the device management (1) or just continue adding new button/fader assignments with the already configured devices (2). If you select 1, you have a few options:

  • 1: Move the assignments from one device over to another. This can help when you plug the controller into another USB port and then shows up under a different name (e.g., "Devicename 1" instead of "Devicename")
  • 2: Delete all devices from the database without removing their mapping. This does exactly that and be warned, will cause a device mixup when you add more devices later. You'll be better of using option 3
  • 3: Remove a single device and their assignments.
  • 4: Add a new device. This allows you do add more devices.
  • 5: Skip device configuration. This exits the device management without changing anything and continues with the assignment dialog.

Understanding input scaling

A MIDI value can be something between 0-127. That is a very limited number.

You will only be asked for Input Scale setup if it's required for the function(SetSourcePosition, SetSourceRotation, SetSourceScale, SetSyncOffset, SetTransitionDuration, SetGainFilter).

The first value you have to enter(lower output value) is the value that will be sent when the fader is sending a 0. The second value you have to enter(higher output value) is the value that will be sent when the fader is sending a 127. The range between the 2 numbers will be interpolated linearly.

Some limitations might apply to the range you can use (see the comments above in the action list above).

"Bidirectional mode" !!ADVANCED/EXPERIMENTAL

THIS IS ONLY FOR ADVANCED USERS THAT ARE COMFORTABLE EDITING CONFIG FILES

If you enable the "bidirectional" mode while setting up SetCurrentScene or SetPreviewScene the script will try to open the input device as an output device and listen for Preview or Program scene change events. It will then send out a note_on or control_change event on midi channel 0 to the same note or control channel that is setup for the specific scene.

The bidirectional mode for the ToggleMute function sends out a note_on with velocity 0 or 2 depending on the mute state and might only work on the AKAI APC mini.

This default approach might not work for some devices like the X-Touch Mini that have different notes/cc values for the same button depending if the data is coming in or going out. In this case you have to add a value named "out_msgNoC" to the config.json file for the button you want to light up with the right note/cc number. To change the default channel you need to add a value named "out_channel" to config.json file.

If the midi out port for your device has a different name then the input port this will also not work without modifying the config.json file. For that first use the device configuration as mentioned above to add another device (could be one with a completely different name, this only saves you the work of manually adding the whole device which you could also do). Then add a value called "out_deviceID" to the button mapping entry with the value set to the id of the output device you just created. Also make sure that the output device name is the right one.

To manually check which MIDI device are available for input/output, run python then paste:

import mido
mido.get_ioport_names()
mido.get_input_names()
mido.get_output_names()

If you want to know more, take a look at the original pull request

Updating MIDItoOBS

As MIDItoOBS is just running from the folder you move/download it into, updating the program itself is (most of the time) as easy as downloading it again like mentioned in Setup Part 3.

I highly recommend that you do not overwrite your existing files but rather backup the folder as is (including the config.json) and start with the freshly downloaded files in a new folder. Then just copy your config.json from the old backup folder into the new folder. Then try to run it.

It can and will happen from time-to-time that I introduce some changes that make the config no longer work with the new program version. As I don't have a changelog yet (which is definitely on the todo list) there is not really any way for you to know. Sometimes I announce such changes on the very top of this readme file. If it no longer works, feel free to open an issue or contact me (See Troubleshooting).

Running MIDItoOBS on another computer in the network

  • You can change the IP and Port of the device running obs and obs-websocket by modifying the main.py (line 5/6) and setup.py (line 6/7) with a text editor. You might have to create some firewall exceptions for the websocket port on the device running obs-websocket.

Setting up "macros" (optional)

You can assign unlimited different actions to the same button. There is no guided GUI way to do this right now so this requires editing the config. (Sounds harder then it is)

  • Setup the functions as described above on different buttons
  • Now stop the setup.py and open the config (config.json) with a text editor.
  • Change the "msgNoC" value of the buttons you want to combine to the value of the button you want to use. Make sure you have the entry with the right device ID.
  • Here are some pictures for better understanding: Step 1 Step 2
  • Now save and close the config file
  • Run main.py and verify that it works

Using it (!Very important!)

  • If you're a first-time user, make sure to follow setup steps 1-3
  • You can launch setup.py anytime (as long as main.py is not running) to change the configuration of a single button/fader without reconfiguring the whole controller.
  • Always make sure that obs is running before launching any of the scripts
  • Launch the main.py file (Try double click or the "Run Main.bat" if you are on Windows)
  • The console gives you information when it successfully connects to OBS
  • Also, if there is an error it will be printed out (If you ignore case-sensitive fields or the scene doesn't exist)
  • Third, it prints out a message every time you press a button that is setup
  • Now just leave it running in the background
  • To stop the program simply close the window (or press CTRL + C)

Command line options

You can call the main.py and the setup.py with the following command line options:

  • --config <path/to/config/file.json> (Default: "config.json")
  • --port <obs-websocket port>(Default: "4444")
  • --host <obs-websocket hostname/ip>(Default: "localhost")

Troubleshooting/Support

A user has reported that under certain circumstances the script(setup and main) will crash after start on Windows with "ImportError: DLL load failed: The specified module could not be found". If this happens to you, please install the Visual C++ Redistributable from Microsoft. Make sure you get the x86 version if you are using python 32-bit (Which is default) (Download)

Contributors

I had never imagined that so many people would contribute something to the project. Thanks to everyone who submitted a bug report or pull request.

Special thanks to:

Tested on/with

  • Windows 10 21H1
  • Ubuntu 18.04
  • Python 3.8.3:6f8c832
  • obs-studio 27.0.1
  • obs-websocket 4.9.1
  • KORG nanoPAD
  • KORG nanoKONTROL 2 (tested by thatGuyStrike and houz)
  • KORG padKONTROL (tested by jberentsson)
  • Behringer FCB-1010 + ESI MidiMate eX (tested by thatGuyStrike)
  • Hercules DJ Control MP3
  • Behringer X-Touch Mini (tested by me-vlad)
  • Behringer X-Touch Compact
  • Arturia MiniLab MKII (tested by moops44). See Issue #17 for notes!
  • Native Instruments Maschine Mk3 (tested by moops44). See Issue #18 for notes!
  • Novation LaunchControl XL (tested by lannonbr)
  • TYST TY-1500HD Switcher (tested by spessoni). Works well, however DSK, PGM, PVW, TRANS EFFECT, and DSK SELECT only really allow one LED to illuminate per group when setting up bidirectional mode. This is generally fine, because you would only be using one button on those groups. Also, you will need to manually update config.json because the MIDI input/output are separate ports.
  • Allen & Heath Xone K2
  • AKAI APC mini
  • AKAI MPK Mini II
  • loopMIDI
  • Pocket MIDI (for debug)
  • StreamDeck Midi Plugin

Let me know if you had success with your device.

This project is not affiliated with the OBS Project or obs-websocket

miditoobs's People

Contributors

alex-dash avatar cpyarger avatar houz avatar imcrazytwkr avatar jberentsson avatar juandelacruz-calvo avatar julxyz avatar lebaston100 avatar lindsaymarkward avatar ptitodd avatar spessoni avatar sprinterfreak avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

miditoobs's Issues

GUI interface

This seems to be a great plugin, But configuration would be much easier with a GUI,

Error when trying to set up a new command

I'm trying to assign new buttons and the Run Setup interrupts when I choose the function I want the fader to call :

Updating scene list, plase wait
Traceback (most recent call last):
File "setup.py", line 1755, in
mainLoop()
File "setup.py", line 1707, in mainLoop
midicallback(msg, device["id"], device["devicename"])
File "setup.py", line 204, in midicallback
setupFaderEvents(action, message.channel, message.control, message.value, message.type, deviceID)
File "setup.py", line 213, in setupFaderEvents
updateSceneList()
File "setup.py", line 1454, in updateSceneList
if jsn["message-id"] == "9999999":
KeyError: 'message-id'

Do you have any idea where it comes from?

(Possibly Mac Only) Filter names in OBS need updating to "filter_v2" in setup.py

Hey again,

Found another small fix that I can turn into a pull request if we confirm whether or not it's a Mac-only Issue.

In short, OBS Filter types received via the websocket are expected to be:
"color_key_filter"
"color_filter"
etc.

but in OBS 27.0.1 are returned as:

"color_key_filter_v2"
"color_filter_v2"
etc.

resulting in setup.py not finding any filters of the type required, even if present.

I've confirmed that simply changing the filterType string in the call to getCompatibleFiltersFromSource fixes the issue for the two filter types listed above, but would need to test the rest of the possible filter types to see if they are all changed in this version of OBS as well.

Hope this is helpful, and seems like an easy enough fix for me to help out with if you're looking for a collaborator. Thanks again Leb,
S

Bidirectional setup KORG nanoKONTROL2

I set up SetCurrentScene to be bidirecitonal and cannot get the LEDs to light at all. Set LED mode to External in Korg Kontrol Edirot. @houz how hed you setup your nanoKontrol 2 for this to work ?

Run on OBS startup

Is there any possibility of this running automatically on OBS startup? I've been messing around a bunch with LUA scripting to see, and have it so that a script runs it, but then OBS just gets hung up because it is waiting for the script to end, and we need it to keep running. Any ideas?

Feature Request: Browser Reset Function

Latest OBS-BrowserSource plugin doesn't reload when OBS-WS ResetSceneItem is issued anymore. But it does reload on browser URL change. So right now I can configure 2 midi buttons to pass different urls to reload a browser source scene item. So I've branched a copy of MIDItoOBS to start work on Making a "RefreshUrl" function. But after reviewing much of the code. I'm not sure where in the main.py script I can append a toggle string ('/?reload=0' and '/?reload=1') to the URL before sending the Websocket message. Any thoughts? Let me know if I need to clarify what I'm doing.

Toggle button not working

I was trying to set up a toggle button as such. the button lights up when pressed once. then turns down when pressed again. thus trying to set up toggle mute. but, it so happens i need to press the button twice for the toggle to happen.

i think the problem is, said button outputs value 127 when pressed first. then value 0 when pressed again. where in the miditoobs, the toggle is expecting 127 each time the button is pressed.

i tried editing config manually but i dont see a field for the value. as in when value 127 mute: true. then create another listener for that midi key with value 0 mute: false.

am i doing something wrong?

Setup Error: AttributeError: 'Element' object has no attribute 'doc_id'

I was trying to use this package today, and I kept getting an error that stated my device might be in use. I had no other applications that I thought could be the culprit. So I opened up the code.

By adding these changes:
++2:import traceback
++1523: traceback.print_exc()

I got a stack trace below:

>python setup.py
MIDItoOBS made by https://github.com/lebaston100

This setup assistant will guide you though the initial setup. If you experience any problems that you can not solve on your own feel free to open an issue on Github

!!Important!!
!!MAKE SURE OBS IS RUNNING OR THIS SCRIPT WILL CRASH!!
!!MAKE SURE THAT THE MIDI DEVICE(S) ARE NOT IN USE BY ANOTHER APPLICATION!!

Please select the number of what you want to do:
1: Re-Setup the midi devices that are used.
2: Leave the selected midi devices as-is and just edit button/fader assignment
Select 1 or 2: 2
Traceback (most recent call last):
  File "C:\Users\chris\Downloads\MIDItoOBS-0.2.5\MIDItoOBS-0.2.5\setup.py", line 1520, in <module>
    tempobj = {"id": device.doc_id, "object": tempmidiport, "devicename": device["devicename"]}
AttributeError: 'Element' object has no attribute 'doc_id'

Could not open device LPD8 0
The midi device might be used by another application/not plugged in/have a different name.
Please close the device in the other application/plug it in/edit the name in the config.json and restart this script.

I'll dig a little further, but wanted to throw this up here in case its a simple fix that you may respond before I can figure it out.

Output Multi-Channel VU

Not very familiar with Midi (yet), but I am considering starting a project to create a Midi control device that also doubles as a VU meter for select audio channels in obs. Looking to see if this plugin can, or might be able to return 6-8 channel audio levels to the connected midi device at a configurable interval. Thank you in advance, and I apologize for my ignorance... also in advance.

Gain Filter Adjustment Not Working

I've got a Korg nanoKontrol2 and I've tried setting multiple knobs to multiple audio sources and their gain filters for adjustment, and I can't get it to work.

Source Group Folder Support

I hacked together some simple support for retrieving Sources from Group folders, if anyone is interested I can throw up a PR

Arturia Minilab MK2 Support

All buttons, knobs, fader works fine with MIDItoOBS.
( Fader 1 & 9 dont work as faders, but as normal buttons cause they are Pushable )
Thanks!

Wrong request in setup.py

Hi. In setup.py the requests for StartRecording and StopRecording are wrong. They send instead StartStreaming and StopStreaming respectively.

BTW, is there a way to ignore the speed of the keyboard? I've noticed that if the speed is too slow it flickers due to sending de signal twice. I can't use the toggle options since they register press and release independently.

Midi Feedback

I currently am using an Xtouch Compact, And It has the ability to take midi feedback for lighting up the buttons, adjusting the motorized faders, etc. It would be nice to have an implementation for sending midi feedback to the controller, that way if I change the scene within the computer, the right scene button lights up, and if I change volume the volume fader updates to the correct position

Request: Ability to control Filter: Render Delay

I would really love to be able to control the delay value on the Render Delay filter with a knob or fader. I think this would be an awesome addition to the already existing filter controls available.

TinyDB Table implementation missing "purge" method (for deleting all devices in setup.py)

Heyo Leb,

See title, unless I'm mistaken. Got the error "'Table' object has no attribute 'purge'" when trying to clear midi devices with devdb.purge(), and sure enough not seeing that in the tinydb 4.3.0 table.py

I'm sort of new to python so might be missing something, but figured I would pass along, in case something changed from previous versions of tinydb. I'd offer to write us a helper function to replace the functionality if I knew with confidence I could figure out how, until then thanks for the app.

Best,
S

Problem with ToggleSourceVisibility when not "current" scene

The ToggleSourceVisibility function is not working if I want to use it on a diffrent scene than the current one.
Useful to toggle the visibility of overlays that are on one "Overlay_Scene" and that one gets included as source on all other scenes.

Error: "specified scene item doesn't exist".
The problem is, that the scene-name is not getting send on the GetSceneItemProperties.
I made a small workaround by using the field2 property, and changed the Template.

main.py changes:

TEMPLATES = {
  "ToggleSourceVisibility": """{
    "request-type": "GetSceneItemProperties",
    "message-id": "%d",
    "item": "%s",
    "scene-name": "%s" # <--- add this line
}""",

config.json example that toggles the visibility of the Source Text_Overlay in the Scene Overlay_Scene:

        "8": {
            "msg_channel": 5,
            "msg_type": "note_on",
            "msgNoC": 51,
            "msgVoV": 127,
            "input_type": "button",
            "action": "{\"request-type\": \"SetSceneItemProperties\", \"message-id\" : \"1\", \"scene-name\": \"Overlay_Scene\", \"item\": \"Text_Overlay\", \"visible\": %s}",
            "request": "ToggleSourceVisibility",
            "target": "Text_Overlay",
            "field2": "Overlay_Scene",
            "deviceID": 1
        }

But with this workaround the scene-name is getting mandatory and therefore the current-Scene option is probably not working any more.
And of course I did not modify the setup.py. I just noticed that it adds only scene and not scene-name to the "action" property in a ToggleSourceVisibility request.

Behringer X-TOUCH MINI support

Hi,
Please add to supported/tested devices Behringer X-TOUCH MINI controller.
All buttons, knobs, fader works fine with MIDItoOBS.
Thanks!

Not Working Correctly With Qcon Pro G2

Hello,
I'm having issues mapping the faders on my console. The script is running in a loop asking me to map as if the console was sending information continuously even if I don't touch anything

Thank you

"OBS disconnected/timed out/is not running." after receiving one input

Hi lebaston100, and first of all thank you for your script.

I followed every steps from your installation guide, but i can't figure out why main.py is shutting down after recieving one input from my midi controller.

OBS registers that input, the sceneswitch works (wich is good sign i guess), but the websocket link seems to be lost right after that.

I'm using :

OBS Studio 22.0.1
Websocket 4.4.0
Python 3.6.5

Control Change values trigger different actions

Hi, I'm working on setting this up.
But, haven't quite figured if it is possible to trigger different actions (say, a "setmute on", and a "setmute off", with the same CC, but a differente value.

So that CC01 with midivalue 00 mutes a source, and CC01 midivalue 127 unmutes it.

Is it possible?

Problems running `setup.py`

Ubuntu 20.04, python 3.8.5, obs-websocket 4.9.0, OBS 26.1.1

I do the following:

  • create a new virtualenv
  • activate the virtualenv
  • pip install -r requirements.txt
  • run obs-studio
  • run python setup.py
(.venv) gdh@gdh-x260:~/MIDItoOBS$ python setup.py 
MIDItoOBS made by https://github.com/lebaston100

This setup assistant will guide you though the initial setup. If you experience any problems that you can not solve on your own feel free to open an issue on Github

!!Important!!
!!MAKE SURE OBS IS RUNNING OR THIS SCRIPT WILL CRASH!!
!!MAKE SURE THAT THE MIDI DEVICE(S) ARE NOT IN USE BY ANOTHER APPLICATION!!


Which device do you want to add?
0: Launchpad Mini:Launchpad Mini MIDI 1 28:0
1: Midi Through:Midi Through Port-0 14:0
Select 0-1: 0
Adding: Launchpad Mini:Launchpad Mini MIDI 1 28:0
Do you want to add another device?
1: Yes
2: No
Select 1 or 2: 2

Could not open device Launchpad Mini:Launchpad Mini MIDI 1 28:0
The midi device might be used by another application/not plugged in/have a different name.
Please close the device in the other application/plug it in/edit the name in the config.json and restart this script.

Running the setup under strace doesn't help much:

openat(AT_FDCWD, "/dev/snd/seq", O_RDWR|O_NONBLOCK|O_CLOEXEC) = 4
ioctl(4, SNDRV_SEQ_IOCTL_PVERSION, 0x7ffec5029638) = 0
ioctl(4, SNDRV_SEQ_IOCTL_CLIENT_ID, 0x7ffec502963c) = 0
ioctl(4, SNDRV_SEQ_IOCTL_RUNNING_MODE, 0x7ffec5029640) = 0
ioctl(4, SNDRV_SEQ_IOCTL_GET_CLIENT_INFO, 0x7ffec5029960) = 0
ioctl(4, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, 0x7ffec5029960) = 0
pipe([5, 6])                            = 0
ioctl(4, SNDRV_SEQ_IOCTL_CREATE_QUEUE, 0x7ffec5029990) = 0
ioctl(4, SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO, 0x7ffec5029a10) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec5029900) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec5029900) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec5029a30) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec5029a30) = -1 ENOENT (No such file or directory)
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec5029900) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec5029a30) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec5029a30) = -1 ENOENT (No such file or directory)
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec5029900) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec5029a30) = -1 ENOENT (No such file or directory)
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec5029900) = -1 ENOENT (No such file or directory)
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec5029770) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec5029770) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec50298a0) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec50298a0) = -1 ENOENT (No such file or directory)
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec5029770) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec50298a0) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec50298a0) = -1 ENOENT (No such file or directory)
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec5029770) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec50298a0) = -1 ENOENT (No such file or directory)
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec5029770) = -1 ENOENT (No such file or directory)
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec50293b0) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec50293b0) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec50294e0) = 0
ioctl(4, SNDRV_SEQ_IOCTL_GET_CLIENT_INFO, 0x7ffec5029590) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec50293b0) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec50293b0) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec50294e0) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec50294e0) = -1 ENOENT (No such file or directory)
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec50293b0) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec50294e0) = 0
ioctl(4, SNDRV_SEQ_IOCTL_GET_CLIENT_INFO, 0x7ffec5029590) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec5029770) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec5029770) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec50298a0) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec50298a0) = -1 ENOENT (No such file or directory)
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec5029770) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec50298a0) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec50298a0) = -1 ENOENT (No such file or directory)
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec5029770) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec50298a0) = -1 ENOENT (No such file or directory)
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec5029770) = -1 ENOENT (No such file or directory)
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec50293b0) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec50293b0) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec50294e0) = 0
ioctl(4, SNDRV_SEQ_IOCTL_GET_CLIENT_INFO, 0x7ffec5029590) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec50293b0) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec50293b0) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec50294e0) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec50294e0) = -1 ENOENT (No such file or directory)
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec50293b0) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec50294e0) = 0
ioctl(4, SNDRV_SEQ_IOCTL_GET_CLIENT_INFO, 0x7ffec5029590) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec5029540) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec5029540) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec5029670) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec5029670) = -1 ENOENT (No such file or directory)
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec5029540) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec5029670) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec5029670) = -1 ENOENT (No such file or directory)
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec5029540) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec5029670) = -1 ENOENT (No such file or directory)
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec5029540) = -1 ENOENT (No such file or directory)
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec5029570) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec5029570) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec50296a0) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec50296a0) = -1 ENOENT (No such file or directory)
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, 0x7ffec5029570) = 0
ioctl(4, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, 0x7ffec50296a0) = 0
ioctl(4, SNDRV_SEQ_IOCTL_CREATE_PORT, 0x7ffec50295f0) = 0
ioctl(4, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, 0xfbae60) = -1 EAGAIN (Resource temporarily unavailable)
write(1, "\nCould not open device", 22

Any ideas here? The ownership of the device node looks fine - I'm a member of the audio group:

(.venv) gdh@gdh-x260:~/MIDItoOBS$ ls -l /dev/snd/seq
crw-rw----+ 1 root audio 116, 1 Mar 11 19:43 /dev/snd/seq
(.venv) gdh@gdh-x260:~/MIDItoOBS$ id
uid=1000(gdh) gid=1000(gdh) groups=1000(gdh),4(adm),20(dialout),24(cdrom),27(sudo),29(audio),30(dip),44(video),46(plugdev),108(kvm),120(lpadmin),131(lxd),132(sambashare),133(docker),137(libvirt)

Cheers,
Gavin.

Actions do not work

I did everything thats said and the setup seemed to work perfectly but when I tried to use them on OBS, it didn't work out ...
Do you know what could I have done wrong ?

Native Instruments MASCHINE MK3 Support

All buttons, knobs, fader works fine with MIDItoOBS.
Thanks!

Note :
Turn on the Device and press Shift + Channel to use Midi
In setup.py, i choosed : devicename": "Maschine MK3 Ctrl MIDI"

will this work with ASIO plugin?

question: will this work ASIO plugins for OBS? If it can't be answered, I may just buy a control surface to test myself, but would like to know before purchasing. If it isn't, maybe a feature request?

Here is the plugin

Thanks!

EXE File

There are Python script to EXE converters which I'm pretty sure don't require installing python 3 and a bunch of packages so it would be easier for people to use if its an EXE file

Change order of scenes

Is it possible to change position of scene? This is interesting for multiview.
Would be great if this is possible.

ImportError: DLL load failed

Just followed your instructions step by step and am receiving the following error when running setup.py

Looks like it cannot find _rtmidi module I will dig into the script to see why it's not working. but something doesn't seem to be working right.

OBS 21.0.1
Websocket Server 4.3.3
Windows 10 Pro v1709 build 16299.248
Python 3.6.5

C:\Users\todd\MIDItoOBS>python setup.py
MIDItoOBS made by lebaston100.de
!!MAKE SURE OBS IS RUNNING OR THIS SCRIPT WILL CRASH!!
Select Midi Device
Traceback (most recent call last):
  File "setup.py", line 537, in <module>
    deviceList = mido.get_input_names()
  File "C:\Users\todd\AppData\Local\Programs\Python\Python36-32\lib\site-packages\mido\backends\backend.py", line 168, in get_input_names
    devices = self._get_devices(**self._add_api(kwargs))
  File "C:\Users\todd\AppData\Local\Programs\Python\Python36-32\lib\site-packages\mido\backends\backend.py", line 161, in _get_devices
    if hasattr(self.module, 'get_devices'):
  File "C:\Users\todd\AppData\Local\Programs\Python\Python36-32\lib\site-packages\mido\backends\backend.py", line 41, in module
    self.load()
  File "C:\Users\todd\AppData\Local\Programs\Python\Python36-32\lib\site-packages\mido\backends\backend.py", line 57, in load
    self._module = importlib.import_module(self.name)
  File "C:\Users\todd\AppData\Local\Programs\Python\Python36-32\lib\importlib\__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "C:\Users\todd\AppData\Local\Programs\Python\Python36-32\lib\site-packages\mido\backends\rtmidi.py", line 8, in <module>
    import rtmidi
  File "C:\Users\todd\AppData\Local\Programs\Python\Python36-32\lib\site-packages\rtmidi\__init__.py", line 4, in <module>
    from ._rtmidi import *  # noqa
ImportError: DLL load failed: The specified module could not be found.

Run setup.py on mac: invalid syntax

Hi, tried to run it on macOs.
I got this message:

$ python setup.py
File "setup.py", line 783
print("Updating "", old_device_name, "" to "", new_device_name, "" now", sep="")
^
SyntaxError: invalid syntax

How to support multiple devices?

For instance, I have a nanoPAD2 which I use to switch scenes with, but I would also like to use the nanoKONTROL2 to control the audio inputs with the faders. Perhaps, having even two nanoPAD2s it would be possible to easily switch between 32 scenes this way.

in code only one device is opened:

self.port = mido.open_input(name=port_name, callback=self.handle_midi_input)

but it is possible to run setup.py multiple times which also creates multiple "type": "device" entries, however none of the commands are bound to a specific device.

Suggestions are welcome... such as merging midi devices to represent itself as a single 'virtual' device?

Studio Mode Crossfader

Hi,

first of all, midi2obs is a wobderful piece of software! Thank you!
I got it to work with my apc mini. Now i am wondering if i can assign the crossfader in the studio mode to one of my faders on the apc.

Greetings from Germany
Ludwig

SetOpacity

Hi, I tried seting up a knob to control opacity.
But when I run setup.py, it doesn't give me that option. Only gives me up to gain filter.

Request: MIDI Forwarding

I'd like to be able to forward MIDI commands to other applications. If I'm understanding it correctly, this would allow me to use some features with MIDItoOBS, like Faders and Volume Adjustments, but then I could use another program like MIDIKey2Key to set global hot keys.

Native hotkey creation would also be cool, but I feel MIDI forwarding would be easier.

Where to store settings for new feature (screenshot location)

I'm having a problem with the location (and possibly name) of saved screenshots. Currently they are stored inside the MIDItoOBS directory and use the current timestamp + png as their filename.
I'd like to add the option to specify a directory so save in but before I do so wanted to ask what would be the best place to add this value: should I add it to the config.json file, or add it as a startup parameter? (like --screenshotdir, but this gets messy if more variables would get added in the future).

Invisible message ID not functioning

Hello. First off, thank you for this script. It is what I was looking for. I setup a midi button to "hide" a render source in OBS, but the script is not functioning. It registers the press, but fails to hide the source in OBS. Also, is there a modification I can implement that will give me toggle abilities? For instance, assigning "visible" and "invisible" to the same midi button and have it toggle between them? Thank you for your hard work.

SetAudioMonitorType Action Returns Error Invalid Request Type

I set a button to change a source's audio monitor to "monitorOnly." When I push the button my my MIDI device, the console reports, "[2020-10-21 22:08:00,312] (ERROR) T15644 : OBS returned error: invalid request type"

Here's my config:

"1": {
            "msg_channel": 0,
            "msg_type": "note_on",
            "msgNoC": 32,
            "msgVoV": 127,
            "input_type": "button",
            "action": "{\"request-type\": \"SetAudioMonitorType\", \"message-id\" : \"1\",\"sourceName\" : \"L1_Spotify\", \"monitorType\": \"monitorOnly\"}",
            "deviceID": 1,
            "bidirectional": false
        },

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.