-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5b7a98b
commit 540c8b8
Showing
1 changed file
with
211 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
# Flapi communication protocol | ||
|
||
The Flapi protocol is a simple protocol for communication between the Flapi | ||
server and Flapi clients. | ||
|
||
Version 1.0. | ||
|
||
## Message format | ||
|
||
Messages are all constructed in the following format: | ||
|
||
| Section | Meaning | | ||
|-----------------------------------|---------| | ||
| [Sysex header](#sysex-header) | Begin MIDI system-exclusive message and identify the message type. | | ||
| [Message origin](#message-origin) | Shows the origin of the message. | | ||
| [Client ID](#client-id) | Unique identifier for client. | | ||
| [Message type](#message-type) | Type of message being sent. | | ||
| Additional data (optional) | Data is dependent by message type, but often uses [status info](#status-info) and [Python encoded data](#python-encoded-data). | | ||
|
||
### Sysex header | ||
|
||
The following values are sent at the start of all messages in order to identify | ||
that the message is associated with Flapi. Messages without this header are | ||
ignored. | ||
|
||
| Byte | Meaning | | ||
|--------|---------| | ||
| `0xF0` | System exclusive message start | | ||
| `0x7D` | Non-commercial use byte number (see the [MIDI 1.0 core specification](https://midi.org/specifications/midi1-specifications/midi-1-0-core-specifications)) | | ||
| `0x46` | `'F'` | | ||
| `0x6C` | `'l'` | | ||
| `0x61` | `'a'` | | ||
| `0x70` | `'p'` | | ||
| `0x69` | `'i'` | | ||
|
||
### Message origin | ||
|
||
One of the listed values is used. | ||
|
||
| Value | Meaning | | ||
|--------|---------| | ||
| `0x00` | Message originates from client. | | ||
| `0x01` | Message originates from server. | | ||
| `0x02` | Message is internal to server (client should disregard). | | ||
|
||
### Client ID | ||
|
||
The ID of the client. This should be randomly generated by the client at the | ||
start of a session, and allows multiple clients to connect simultaneously. | ||
Clients should ignore responses targeting a different client ID. | ||
|
||
This should be a random byte, with a value between `0x01` and `0x7F`. | ||
The ID `0x00` is reserved for universal messages, which target all clients. | ||
|
||
Upon receiving a request with a given client ID, the server will give a | ||
response with the same client ID. | ||
|
||
### Message type | ||
|
||
The final common byte describes the type of message. The value can be one of | ||
the following values. Other values are reserved for future Flapi versions. | ||
|
||
| Value | Meaning | | ||
|--------|---------| | ||
| `0x00` | [Client hello](#client-hello) | | ||
| `0x01` | [Client goodbye](#client-goodbye) | | ||
| `0x02` | [Server goodbye](#server-goodbye) | | ||
| `0x03` | [Version query](#version-query) | | ||
| `0x04` | [Exec](#exec) | | ||
| `0x05` | [Eval](#eval) | | ||
| `0x06` | [Stdout](#stdout) | | ||
|
||
The following sections describe each of these message types. | ||
|
||
### Status info | ||
|
||
Many data formats for message types use a status code within the response, | ||
which indicates the results of the operation. This typically has additional | ||
data | ||
|
||
| Code | Meaning | Typical additional data | | ||
|--------|---------|-------------------------| | ||
| `0x00` | Success | Usually [Python encoded data](#python-encoded-data), depending on the message type. | | ||
| `0x01` | An exception was raised during the operation | [Python encoded data](#python-encoded-data) of the exception. The exception is raised within the default client. | | ||
| `0x02` | The server failed to process the request | [Python encoded data](#python-encoded-data) of the error message. Default client raises this message as a `FlapiServerError`. | | ||
|
||
## Python encoded data | ||
|
||
In order to transfer data between the client and the server, the following | ||
system is used. | ||
|
||
* Python objects are encoded using `pickle` | ||
* The resulting string is encoded using base-64 | ||
|
||
In code, this can be represented as: | ||
|
||
```py | ||
import pickle | ||
from base64 import b64encode | ||
|
||
data = { "a": 1, "b": 2, "c": 3 } | ||
|
||
# Encode | ||
encoded = b64encode(pickle.dumps(data)) | ||
|
||
# Decode | ||
decoded = pickle.loads(b64decode(encoded)) | ||
|
||
assert data == decoded | ||
``` | ||
|
||
### Client hello | ||
|
||
This message is sent when a client wants to connect to the Flapi server. For | ||
client hello messages, no additional data is present. | ||
|
||
This message type is the only message type to which the server may not respond. | ||
In particular, the server will not respond if that client ID is already in use | ||
by another active client. If a client does not receive a response, it should | ||
generate a new client ID and try again. If the client repeatedly doesn't | ||
receive a response, it is likely that FL Studio isn't running (or there are | ||
tens of thousands of clients connected to the Flapi server, meaning all | ||
available client IDs are taken). | ||
|
||
If the connection is accepted, the server will give an empty response targeted | ||
to the newly connected client. This means the server has registered that client | ||
ID session. | ||
|
||
### Client goodbye | ||
|
||
This message is sent when a client is exiting, and wants to disconnect from the | ||
Flapi server. As additional data, it contains an `int` exit code as | ||
[Python encoded data](#python-encoded-data). | ||
|
||
The server will respond with an identical reply. The default Flapi client | ||
(provided by this library) raises a `FlapiClientExit` exception when this | ||
happens, which will (by default) cause the program to exit with the given exit | ||
code. | ||
|
||
### Server goodbye | ||
|
||
This message should never be sent by clients. Instead, it is sent by the server | ||
when FL Studio is exiting. The default Flapi client raises a `FlapiServerExit` | ||
exception when this happens, which (by default) is not handled. | ||
|
||
### Version query | ||
|
||
This message is sent by clients to query the version of the Flapi server. | ||
|
||
The server will respond with 3 bytes as the message data: | ||
`(major, minor, revision)`. | ||
|
||
### Exec | ||
|
||
This message is sent by clients, and instructs the server to `exec` code inside | ||
FL Studio. Unlike `eval`, `exec` does not produce a return value, and so should | ||
be used to execute statements, not expressions. | ||
|
||
#### Exec request data | ||
|
||
[Python encoded data](#python-encoded-data) containing a string with code to | ||
execute. | ||
|
||
#### Exec response data | ||
|
||
[Status info](#status-info). For success, no data is given. | ||
|
||
### Eval | ||
|
||
This message is sent by clients, and instructs the server to `eval` code inside | ||
FL Studio. Unlike `exec`, `eval` produces a return value, and so should be used | ||
to evaluate expressions, not statements. | ||
|
||
#### Eval request data | ||
|
||
[Python encoded data](#python-encoded-data) containing a string with code to | ||
evaluate. | ||
|
||
#### Eval response data | ||
|
||
[Status info](#status-info). For success, the return value of `eval` is given. | ||
|
||
### Stdout | ||
|
||
This message is sent from the server to the client to notify it of `stdout` | ||
originating from FL Studio's console. This can be sent at any time, and so | ||
clients should be prepared to handle it at any point when receiving a message. | ||
|
||
It contains [Python encoded data](#python-encoded-data) of the string that was | ||
printed to stdout. | ||
|
||
## Example | ||
|
||
To help clarify, here is an example demonstrating the basic functionality of | ||
the protocol. | ||
|
||
Messages are shown as a list of bytes in hexadecimal, with spaces used to | ||
show different sections of the message. | ||
|
||
| Origin | Message | Meaning | | ||
|--------|---------------------------------------------------------|---------| | ||
| Client | `F07D466C617069` `00` `01` `00` | Client requests to connect using client ID `01` | | ||
| Server | `F07D466C617069` `01` `01` `00` | Server responds, accepting the connection | | ||
| Client | `F07D466C617069` `00` `01` `03` | Client requests server version information | | ||
| Server | `F07D466C617069` `01` `01` `03` `010000` | Server responds, saying it is using version `1.0.0` | | ||
| Client | `F07D466C617069` `00` `01` `04` `67415...3554C673D3D` | Client requests to execute `import transport` | | ||
| Server | `F07D466C617069` `01` `01` `04` `00` | Server responds, indicating success | | ||
| Client | `F07D466C617069` `00` `01` `05` `67415...7706C43343D` | Client requests to evaluate `transport.start()` | | ||
| Server | `F07D466C617069` `01` `01` `05` `00` `6741524C4343343D` | Server responds, indicating a result of `8` | | ||
| Client | `F07D466C617069` `00` `01` `01` `6741524C4143343D` | Client disconnects with a code of `0` | | ||
| Server | `F07D466C617069` `01` `01` `01` `6741524C4143343D` | Server acknowledges disconnect | |