Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time

Usage as a library in a program

Feel free to use the python part of the JAM experiment as an example.

General information

Settings

You can use your own settings dict (see below for details) although it is not recommended to create one from scratch. You can however instantiate SettingsParser by yourself and freely manipulate the settings dict (but pay attention not to change the types of the settings) and pass it on when you instantiate Main.

Logging

The logging system is built to be flexible. You can use or own logger, use one derived from the programs internal logger or something in between. Notice the difference between 'MRKeyboard' (the class) and 'keyboard' (the object you get from the class when instantiating it).

Do not change the log level for the whole logger object as we may use the high verbosity logger and this would overwrite its lof level redering it useless. It's best to decide on the level you want to use before instantiating mr_keyboard and to stick with it.

Using the programs logger

Don't pass a logger when instantiating MRKeyboard and use keyboard.log (or better: derive your own logger from it with keyboard.create_child_logger()).

Create you own logger

Feel free to use the static functions in MRKeyboard to create a logger (MRKeyboard.get_console_logger()) and attach the file handler if you like with MRKeyboard.attach_file_handler_to_logger() (or keyboard.attach_file_handler_to_logger_from_settings() if you have already instantiated Main and want the file handler configured as described in the settings file and flags).

Integration into the lifecycle of the program

The integration into the stages of the program is for the most part done with callbacks but you're not forced to use them in most places.

Init/Start

You have two options:

  • register a callback (or more than one) with keyboard.callbacks_start.add() and call keyboard.start() when you're ready to connect to the other devices (the keyboard, midi devices).
  • do the init in your experiment independent of the program/library (i.e. without using callbacks) before calling loop()

The main loop

Here you only have one option: use the callback. You can register it with keyboard.callbacks_loop.add().

The cleanup routine

Basically the same as in Init/Start. The function to register the callback is keyboard.callbacks_cleanup.add() but you may as well do your own cleanup routine without the callbacks at some point after loop() is done.

Using the program in your own code

  • read the section below, it should give you a feeling where to find the methods you need
  • fork (the button called "fork" in the top right corner on the github page of the repo) the repository and make a pull request if you've made changes to the core of the program (i.e. everything besides your experiment)
  • you should only need to modify you own code that uses the program as a library, if this is not sufficient then just add a function for the specific thing you want to do and use it in your code. Never add code specific to your experiment to the other parts of the program!
  • you should know the basics of programming in python, most important are the following:
    • scopes: https://python-textbok.readthedocs.io/en/1.0/Variables_and_Scope.html#variable-scope-and-lifetime
    • everything starting with _ is private and should not be used from outside of its scope (names ending in one are just to avoid shadowing something else)
    • basic knowledge on how to debug a program. Some quick hints:
      • try to answer the following questions as good as you can:
        1. What makes you say your code isn't working?
        2. What did you expect your code to do and why?
        3. What did your code do instead and how do you know?
      • use a good IDE (e.g. PyCharm) with a debugger and use breakpoints
      • use print() for quick and dirty debug output only
      • dir() is quite handy if you want to know whats available in the current scope or what methods an object has (see the docs for more details)
  • the program adheres to a specific code style based on PEP8 with a one exception: the indentation is done with tabs instead of spaces. Hanging indents are avoided where possible Some specific lines don't stick to PEP8, those have (in most cases) a comment starting with # noinspection directly above them (the comment disables a specific style check in PyCharm so we don't get a warning)

Structure of the program

Files

The program consists of the following basic classes (the names of the files are very similar to the class names, the indentation hints on what instantiates/starts what):

  • MRKeyboard (the main class and the entry point for the program)
    • Optoboard (handles the connection to a single board)
      • OptoboardChannel (interprets the data for a single channel of a single board)
    • MidiHandler (handles all of the midi stuff)
    • SettingsParser (takes care of all of the settings)

Additionally there are a couple of files (the ones ending ind .md are markdown formatted files):

  • settings.ini (contains most of the settings, some are only available in the CLI)
  • constants.py (the constants we use in the program, feel free to import them as well if you want to use the same custom debug levels)
  • README.md (a short intro to the program)
  • USAGE_GENERAL.md (this document)
  • CHALLENGES.md (a couple of tasks if you want to familiarise yourself with the program)
  • .gitignore (not relevant for you, tells git what it should ignore. git is a version control system.)

Settings

The settings are present in the program in the form of a 2D-nested dict (accessing it looks like this: settings['<category>']['<setting>']) generated from the settings file and the arguments passed to the CLI. The complete content of the current settings is displayed right at the start of the program (use at least -vv in the CLI to see it). Note that the settings dict is not a global object! It is usually passed to each object when instantiating it.

The structure of the settings is very close to the general structure of 'settings.ini', there you can also find hints on what data type to expect.

Lifecycle of the objects

Each of the objects has a similar lifecycle. These are the stages (and the most notable functions used there):

  • initialisation (__init__() or start() )
  • loop or get updated by the loop (loop() or update() )
  • cleanup (cleanup() or close() )

Lifecycle of the program

The lifecycle of the whole program is very similar to the lifecycle of the objects (no coincidence: Main is one of the objects as well and the lifecycle of Main is basically the lifecycle of the whole program). It generally looks like this:

  • initialisation
    • init the logger(s)
    • check/configure the network
    • init the boards
    • init midi
    • call the callbacks for setup
  • loop
    • (not in the loop but in the function loop() ) do a bit of preparation and then enter the loop
    • collect the updates from the boards
      • in Optoboard:
      • collect the data from the boards
        • in OptoboardChannel:
        • interpret the data for every channel
      • return any events that happened (i.e. key presses or releases)
    • trigger midi events accordingly (i.e. send 'note_on' or 'note_off' to the midi output) and handle the events from the secondary midi device
    • call the callbacks for the loop
    • calculate performance data and sleep a bit to limit the cycle frequency
  • cleanup
    • calculate performance data for the whole runtime
    • close the connection to the midi devices
    • close the connection to the boards
    • call the callbacks for cleanup
    • flush the loggers
    • exit

Actually using the program as a module

Import the file (import mr_keyboard) and instantiate MRKeyboard with the options you like.