Skip to content

Commit

Permalink
Merge pull request #3 from MiguelGuthridge/migue/docs
Browse files Browse the repository at this point in the history
Add documentation
  • Loading branch information
MaddyGuthridge authored Feb 12, 2022
2 parents 0caf8ca + 28ee0ec commit 3dba166
Show file tree
Hide file tree
Showing 30 changed files with 686 additions and 48 deletions.
15 changes: 14 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,33 @@
"buttondatastrat",
"capsys",
"Cimbalom",
"classmethod",
"controlmatcher",
"controlsurfaces",
"deinitialise",
"deviceconfigs",
"deviceshadow",
"dicttools",
"docstrings",
"Dpad",
"eventfilter",
"eventfilters",
"eventpattern",
"flmidimsg",
"ieventpattern",
"ivaluestrategy",
"launchkey",
"mappingstrategy",
"Maudio",
"MIDIIN",
"Novation",
"pmeflags",
"RRGGBB",
"Sostenuto",
"staticmethod",
"strat",
"sysex",
"Tytel"
"Tytel",
"valuestrategy"
]
}
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
# Universal-Controller-Script
A script aimed at adding compatibility for any MIDI controller in FL Studio
A script aimed at adding compatibility for any MIDI controller and any plugin in
FL Studio.

# WARNING: This script is currently not functional
# Setup

This is a complete rewrite from the ground up of a concept script I made for the same purpose. Eventually, it should greatly surpass all of my previous work for the legacy version, but it is currently non-functional. If you wish to look at the original, please head to [MiguelGuthridge/Legacy-Universal-Controller-Script](https://github.com/MiguelGuthridge/Legacy-Universal-Controller-Script).
Refer to the [documentation](docs/setup.md).

If you have any ideas for the development of the script, or want to contribute, please join the Discord Server [here](https://discord.gg/6vpfJUF). I'm excited to see where this project will go in the future: I have huge plans for it!
# Documentation

Documentation is available [here](docs/README.md), and contains information for
users, as well as contributors.

If you have any ideas for the development of the script, or want to contribute,
please [join the Discord Server](https://discord.gg/6vpfJUF). I'm excited
to see where this project will go in the future: I have huge plans for it!
10 changes: 5 additions & 5 deletions controlsurfaces/navigation.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ class NavigationControl(ControlSurface):
def __init__(self, event_pattern: IEventPattern, value_strategy: IValueStrategy) -> None:
super().__init__(event_pattern, value_strategy, "navigation")

class NavigationButtons(Button, NavigationControl):
class NavigationButton(Button, NavigationControl):
"""
Navigation buttons are used to navigate FL Studio
"""

class DpadButtons(NavigationButtons):
class DpadButtons(NavigationButton):
"""
D-pad buttons are used to navigate FL Studio with directional inputs
"""
Expand Down Expand Up @@ -54,17 +54,17 @@ class DirectionSelect(DpadButtons):
A select button (usually in the centre of a d-pad)
"""

class NextPrevButtons(NavigationButtons):
class NextPrevButton(NavigationButton):
"""
Represents next or previous buttons
"""

class DirectionNext(NextPrevButtons):
class DirectionNext(NextPrevButton):
"""
A next button
"""

class DirectionPrevious(NextPrevButtons):
class DirectionPrevious(NextPrevButton):
"""
A previous button
"""
25 changes: 14 additions & 11 deletions devices/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,12 @@ def getId() -> str:
def getUniversalEnquiryResponsePattern() -> Optional[IEventPattern]:
"""
Returns the event pattern from which a device can be recognised so that
its representation can be loaded
its representation can be loaded, or None, if this device can't be
matched using this pattern.
### Returns:
* `IEventPattern`: pattern to match universal device enquiry
* `IEventPattern`: pattern to match universal device enquiry, or None if
can't be matched.
"""
raise NotImplementedError("This method must be overridden by child "
"classes")
Expand All @@ -89,7 +91,7 @@ def getUniversalEnquiryResponsePattern() -> Optional[IEventPattern]:
def matchDeviceName(name: str) -> bool:
"""
Returns whether this device matches the name given, where the name is
the return value of `device.getName()`
the return value of `device.getName()`.
This is used as a fallback for matching the device if no universal
device enquiry response is given.
Expand All @@ -103,14 +105,7 @@ def matchDeviceName(name: str) -> bool:
raise NotImplementedError("This method must be overridden by child "
"classes")

def initialise(self) -> None:
"""
Called when the device is first recognised, and when FL Studio allows
communication.
Can be overridden by child classes.
"""

@abstractmethod
@staticmethod
def getDrumPadSize() -> tuple[int, int]:
"""
Expand All @@ -124,6 +119,14 @@ def getDrumPadSize() -> tuple[int, int]:
"""
return 0, 0

def initialise(self) -> None:
"""
Called when the device is first recognised, and when FL Studio allows
communication.
Can be overridden by child classes.
"""

# def deinitialise(self) -> None:
# """
# Called when FL Studio is going to start blocking communication, such as
Expand Down
3 changes: 2 additions & 1 deletion devices/matchers/controlmatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ def matchEvent(self, event: eventData) -> Optional[ControlMapping]:
@abstractmethod
def getGroups(self) -> set[str]:
"""
Return a set of groups for all the control surfaces.
Return a set of groups for all the control surfaces managed by this
matcher.
Refer to the documentation for the group property in the ControlSurface
type.
Expand Down
2 changes: 1 addition & 1 deletion devices/maudio/hammer88pro/hammer88pro.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ def getUniversalEnquiryResponsePattern():
0x00, # Family code
0x3C, # Family code
# Extra details omitted
]
]
)

@staticmethod
Expand Down
25 changes: 12 additions & 13 deletions devices/novation/launchkey/mk2/launchkey.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from common.eventpattern import BasicPattern
from common.types import eventData
from common.extensionmanager import ExtensionManager
from controlsurfaces.valuestrategies import Data2Strategy
from controlsurfaces.valuestrategies import Data2Strategy,ButtonData2Strategy
from devices import Device, BasicControlMatcher
from controlsurfaces.controlgenerators import getNotesAllChannels

Expand Down Expand Up @@ -49,42 +49,40 @@ def __init__(self, matcher: BasicControlMatcher) -> None:
# Transport
matcher.addControl(StopButton(
BasicPattern(0xB0, 0x72, ...),
Data2Strategy()
ButtonData2Strategy()
))
matcher.addControl(PlayButton(
BasicPattern(0xB0, 0x73, ...),
Data2Strategy()
ButtonData2Strategy()
))
matcher.addControl(LoopButton(
BasicPattern(0xB0, 0x74, ...),
Data2Strategy(),
ButtonData2Strategy(),
))
matcher.addControl(RecordButton(
BasicPattern(0xB0, 0x75, ...),
Data2Strategy()
ButtonData2Strategy()
))
matcher.addControl(DirectionNext(
BasicPattern(0xB0, 0x66, ...),
Data2Strategy()
ButtonData2Strategy()
))
matcher.addControl(DirectionPrevious(
BasicPattern(0xB0, 0x67, ...),
Data2Strategy(),
ButtonData2Strategy(),
))
matcher.addControl(RewindButton(
BasicPattern(0xB0, 0x70, ...),
Data2Strategy(),
ButtonData2Strategy(),
))
matcher.addControl(FastForwardButton(
BasicPattern(0xB0, 0x71, ...),
Data2Strategy(),
ButtonData2Strategy(),
))
matcher.addControl(StandardPitchWheel())
matcher.addControl(StandardModWheel())

super().__init__(
matcher
)
super().__init__(matcher)

@staticmethod
def getDrumPadSize() -> tuple[int, int]:
Expand All @@ -103,10 +101,11 @@ def __init__(self) -> None:
Fader(
BasicPattern(0xB0, 0x28 + i, ...),
Data2Strategy(),
(i, 0)
(0, i)
)
)
# Master fader
# TODO: Make this separate
matcher.addControl(
Fader(
BasicPattern(0xB0, 0x07, ...),
Expand Down
6 changes: 6 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

# Documentation

* [Setup Instructions](setup.md)
* [Information for Contributors](contributing/README.md)
* [Device Information](devices/README.md)
9 changes: 9 additions & 0 deletions docs/contributing/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

# Contributing

## Getting Started

* Get familiar with the project's [style guidelines](style.md)
* This documentation is only of overarching designs and how-tos. Code in the
project is documented using docstring, and will be displayed inline by most
code editors.
37 changes: 37 additions & 0 deletions docs/contributing/controlmatcher.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@

# Control Matchers

Control matchers are used to maintain a set of control surfaces, which can
be matched with incoming events.

## `IControlMatcher`

The interface used by control matchers. If the `BasicControlMatcher` doesn't
suit your needs, you can implement this interface to create your own control
matcher.

### Methods to Implement
* `matchEvent(self, event: eventData) -> Optional[ControlMapping]`: Given an
event, return a mapping to a matched control, or `None` if there were no
matches.
* `getGroups(self) -> set[str]`: Return the set of control groups this control
matcher uses.
* `getControls(self, group:str=None) -> list[ControlSurface]`: Return a list of
the controls managed by this control matcher.

## `BasicControlMatcher`

A basic control matcher that can be used for most devices. It provides various
other methods for managing controls

* `addControl(self, control: ControlSurface)`: Registers a control surface to
the matcher.
* `addControls(self, controls: list[ControlSurface])`: Registers a list of
control surfaces to the matcher.
* `addSubMatcher(self, matcher: IControlMatcher)`: If a small amount of
complexity is required with control matching, the basic matcher may not be
sufficiently powerful. This function can be used to add another
IControlMatcher to act as a component of this matcher. An example of this can
be seen in the implementation of the jog wheel on the M-Audio Hammer 88 Pro,
where the sub-matcher is used to make events map to a different type of jog
wheel depending on whether the encoder is pressed down or not.
67 changes: 67 additions & 0 deletions docs/contributing/controlsurface.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@

# Control Surfaces

Control surfaces represent a control on a device. The are instantiated during
the construction of `Device` objects, and are mapped to by plugins.

## List of Control Surfaces

* `Note`: Represents a note event
* `ModWheel`: Represents a modulation wheel
* `PitchWheel`: Represents a pitch bend wheel
* `AfterTouch`: Represents aftertouch events
* `ChannelAfterTouch`
* `NoteAfterTouch`
* `Pedal`: Represents a foot pedal
* `SustainPedal`
* `SostenutoPedal`
* `SoftPedal`
* `Button`: Represents a button (used by many transport controls)
* `JogWheel`: Represents an encoder used for transport and navigation
* `StandardJogWheel`: Scrolling and changing selection
* `MoveJogWheel`: Moving selection
* `TransportButton`: Buttons used for transport
* `PlayButton`
* `StopButton`
* `LoopButton`: Toggle FL Studio's loop mode
* `RecordButton`
* `FastForwardButton`
* `RewindButton`
* `MetronomeButton`
* `NavigationButton`: Buttons used for navigating FL Studio
* `DpadButtons`: Buttons used for directions
* `DirectionUp`
* `DirectionDown`
* `DirectionLeft`
* `DirectionRight`
* `DirectionSelect`
* `NextPrevButton`: Next and previous buttons
* `DirectionNext`
* `DirectionPrevious`
* `Fader`: Represents a fader (linear slider)
* `Knob`: Represents a knob (rotating dial)
* `Encoder`: Represents an encoder (endlessly rotating dial)
* `DrumPad`: Represents a drum pad

## Creating New Control Surfaces

As a general rule of thumb, new control surfaces types shouldn't be created
except for those that don't match any existing types. This should be discussed
in the Discord server before creating one. This is because having more control
surface types will make it harder to assign controls easily within plugins.

## Extending Existing Control Surfaces

By default, the provided control surfaces don't provide any advanced
functionality such as colour or annotation support. If a control on your device
supports this, or requires logic that doesn't work well with the parent class
(such as the M-Audio Hammer 88 Pro's pitch wheel), it should implement it in a
child class to the control surface it most accurately represents, and then
implement any required functions.

### Methods to Implement if Required
* `onColorChange(self)`: Called when the color of the control has changed
* `onAnnotationChange(self)`: Called when the annotation of the control has
changed.
* `onValueChange(self)`: Called when the value of the control has changed.
* `tick(self)`: Called when a tick happens.
Loading

0 comments on commit 3dba166

Please sign in to comment.