Pisound Micro Mapper#
Pisound Micro Mapper is a program running in the background that takes care of initializing Pisound Micro controls according to the provided configuration file and can also translate them to MIDI, Osc or other common audio software communication means as well as map them to the ALSA mixer controls.
The source code is licensed under GPL 2.0.
Install#
Assuming that you're using Patchbox OS image, or have set up our APT server, run the following command to install:
sudo apt install -y pisound-micro-mapper
Configuration#
The configuration is defined in a json file, by default located at /etc/pisound-micro-mapper.json
.
Upon modifying the configuration, you must restart the server for changes to take place:
sudo systemctl restart pisound-micro-mapper.service
Json is a text based format with strict syntax rules, here's a quick tutorial to get you started. Only valid json files are accepted, but there's tools available to help get them right. For example, there's an online JSONLint tool which may verify the syntax of your file, or you can use a text editor with built-in validation, such as Visual Studio Code or Sublime Text for editing.
They can even offer auto-completion of values.
A Basic Example#
Let's dive right in and look at a minimal, but complete, working configuration example:
{
"$schema": "https://blokas.io/json/pisound-micro-schema.json",
"version": 1,
"controls": {
"pisound-micro": {
"volume_encoder": {
"type" : "encoder",
"pins" : [ "B03", "pull_up", "B04", "pull_up" ]
}
},
"alsa": {
"hw:pisoundmicro": [
"Digital Playback Volume"
]
}
},
"mappings" : [
[ "Digital Playback Volume", "<->", "volume_encoder" ]
]
}
In the controls
section, there's one encoder defined, connected to Pisound Micro's pins B03 and B04, as well as an ALSA mixer control of Pisound Micro which we'll want to manipulate. The mappings
section creates a two way mapping between the controls, so that both of the controls are kept in sync. Whenever the encoder is turned, the ALSA mixer control will change accordingly, as well as whenever the ALSA mixer control gets changed directly, it will be reflected in the encoder's value
attribute, so value jumps are avoided the next time the encoder is turned.
Structure#
Let's take a look at the bare bones skeleton:
{
"$schema": "https://blokas.io/json/pisound-micro-schema.json",
"version": 1,
"controls": {},
"mappings": []
}
This is the root of the configuration.
$schema
defines the JSON Schema to use for validation of data. Advanced editors such as Visual Studio Code or Sublime Text can verify the validity of your config data in real time and also provide suggestions for property names and their possible values.version
is the version of the configuration structure. Should be1
.controls
is where all of the controls are defined.mappings
is where controls can be tied together to affect one another, it's a key place, if you'd like to assign say an analog potentiometer connected to Pisound Micro to control the playback volume.
Controls#
This section contains control configuration information for a couple of control subsystems, each is defined in further subsections.
The json examples shown here should be assumed to be inside "controls": {}
object. All of the subsystems are optional, you should configure only the ones you intend to use.
Pisound Micro#
To gain a good understanding of the possibilities for Pisound Micro controls, first read through the Sysfs Interface which establishes the key concepts.
Each Pisound Micro control is defined as an object within the container "pisound-micro": {}
object. The keys are the Pisound Micro Element names to use. These names will have to be used in the mappings
section later on. The type
property is essential, the remaining set of properties that can be set depend on it.
Here's a quick example on how an Analog Input should be configured:
...
"pisound-micro": {
"control_name": {
"type": "analog_in",
"pin": "B23"
}
}
...
The control_name
above can be any string of up to 63 bytes in length, as long as it does not contain /
characters, it is used as the Pisound Micro's Element name, as well as to refer to the control within the mappings
section.
Reference#
Here's all of the control properties as well as the enumerations for configuring Pisound Micro controls:
Properties#
Property | Type | Applicable to Type | Description |
---|---|---|---|
type |
Type enum | All | The type of the control, see below. |
pin |
Pin enum | All, except GPIO Input and Encoder | The pin of the control. |
pin |
Array | GPIO Input | An array of 2 members - [ pin, pull ]. |
pins |
Array | Encoder | An array of 4 members - [ pin A, pin A pull, pin B, pin B pull. ] |
activity |
Activity enum | Activity | The activity to monitor. |
value |
Boolean | GPIO Output | The initial output value. |
mode |
Mode enum | Encoder | (Optional) The behavior of the value at the boundary. |
input_min |
Number | Analog Input & Encoder | (Optional) The minimum value bound. |
input_max |
Number | Analog Input & Encoder | (Optional) The maximum value bound. |
value_low |
Number | Analog Input & Encoder | (Optional) The lower boundary to linearly rescale to. |
value_high |
Number | Analog Input & Encoder | (Optional) The higher boundary to linearly rescale to. |
Type Enum#
Enum Entry | Description |
---|---|
gpio_input |
GPIO Input. |
gpio_output |
GPIO Output. |
activity |
Activity. |
encoder |
Encoder. |
analog_in |
Analog Input. |
Pin Enum#
Enum Entry | Description |
---|---|
A27 ... A32 |
GPIO pin. |
B03 ... B18 |
GPIO pin, recommended for Encoders. |
B23 ... B34 |
GPIO pin, can read Analog Input. |
B36 ... B39 |
GPIO pin. |
Pull Enum#
Enum Entry | Description |
---|---|
pull_none |
No pull, Hi-Z state. |
pull_up |
Pull up. |
pull_down |
Pull down. |
Activity Enum#
Enum Entry | Description |
---|---|
midi_in |
Indicate activity on events coming into MIDI Input port. |
midi_out |
Indicate activity on events based on the MIDI Output port. |
Mode Enum#
Enum Entry | Description |
---|---|
clamp |
(Default) The Encoder's value will stay within the boundaries. |
wrap |
The Encoder's value will wrap around to the other boundary. |
ALSA#
The alsa
object within the controls
object is used to declare which ALSA mixer controls will be involved in the mappings. To discover the names of the ALSA mixer controls, run the following command:
amixer -D hw:pisoundmicro controls
It will output all of the control names, in particular, we're interested in the contents of name='...'
, for example:
...
numid=1,iface=MIXER,name='Digital Capture Volume'
numid=2,iface=MIXER,name='Digital Playback Volume'
...
To bring these controls into Pisound Micro Mapper's attention, they can be declared like this:
...
"alsa": {
"hw:pisoundmicro": [
"Digital Playback Volume",
["Digital Capture Volume", { "alias": "rec_vol" } ]
]
}
...
The above example demonstrates the 2 possible ways of listing the ALSA mixer controls - the first way is using the control name verbatim. The 2nd way lets you define an alias for the mixer control name. As you're allowed to involve other ALSA cards as well by placing them within the alsa
object, control name conflicts may arise, thus aliases can be used to circumvent the name conflicts.
MIDI#
One or more virtual ALSA sequencer MIDI ports can be configured to be mapped to/from the other controls.
Refer to Summary of MIDI 1.0 Messages for a quick overview of the MIDI messages.
Consider the following example, within the controls
object:
...
"midi": {
"my_port": {
"controls": {
"fader": { "type": "control_change", "channel": 1, "id": 3 },
"pad": { "type": "note_on", "channel": 1, "id": 60 }
}
}
}
...
my_port
is the ALSA sequencer port name to use, it can be referred to from other software or CLI commands, for example, running aconnect -l
would output:
...
client 130: 'my_port' [type=user,pid=14584]
0 'my_port '
...
You may then refer to this port either by 130:0
or my_port:0
when configuring other software.
The MIDI messages that this port should listen to and produce must be listed in the controls
sub-object, just like in the above example. It should contain an object for each control, the keys are the control name which will be used later on in the mappings
section.
Each control must have a type
property. Based on the type, some controls must also have id
(number between 0-127) and/or channel
(number, 1-16) properties. See the below table for the possible type
values:
Type | Channel? | Id? | Description |
---|---|---|---|
note |
Y | Y | Note On and Note Off |
note_on |
Y | Y | Note On |
note_off |
Y | Y | Note Off |
control_change |
Y | Y | Control Change |
program_change |
Y | Y | Program Change |
poly_aftertouch |
Y | Y | Polyphonic Aftertouch |
pitch_bend |
Y | Pitch Bend | |
channel_pressure |
Y | Channel Pressure | |
start |
Start | ||
continue |
Continue | ||
stop |
Stop | ||
reset |
Reset |
*Y marks whether Channel or Id are required properties for the type.
The Midi Note Numbers should be expressed as integer numbers. There's some differing standards on which note is the 'Middle C', usually it's the note 60, but occasionally it can be one octave higher (+12 semitones (72)) or lower (-12 semitones (48)), in case of issues matching up the number to external MIDI controllers, use MIDI monitoring software such as aseqdump
or refer to you controller's manual.
OSC#
Open Sound Control messaging can be used as well. Consider this example, assume it's within the controls
object:
...
"osc": {
"my_synth": {
"listen": "osc.udp://:9000",
"notify": [ "osc.udp://:9001", "osc.udp://:9002" ],
"params": {
"cutoff_freq0": {
"path": "/filter/0/cutoff_freq",
"type": "f",
"low": 0.0,
"high": 20000.0
},
"waveform0": {
"path": "/oscillator/0/waveform",
"type": "i",
"low": 0,
"high": 7
},
"rec_vol_right": {
"path": "/alsa/1/rec_volume",
"type": "f"
}
}
}
}
...
my_synth
is an arbitrary name, you can set it to anything that's meaningful to you. The object defines a port to listen to for changes, as well as which other OSC destinations should be notified of changes. The params
object contains the OSC parameter definitions, the type
and path
properties are required, low
and high
are optional and default to 0.0
and 1.0
for floating point type (f
) or 0
and 1023
for integer type (i
). The names will be used to refer to from the mapping
section later on.
Reference#
Control Server Properties#
Property | Type | Description |
---|---|---|
listen |
Address | (Optional) The listen address. |
notify |
Address or an array of addresses | (Optional) The destination(s) to send notifications to. |
params |
Object containing Parameter sub-objects | The Parameters. |
Address#
An Address is a liblo URL string, consisting of protocol (udp or tcp), IP and port number, some examples:
osc.udp://:9000
- localhost, UDP port 9000.osc.tcp://:9000
- localhost, TCP port 9000.osc.udp://192.168.1.123:5678
- Another device in local network with IP 192.168.1.123, UDP port 5678.
Parameter Properties#
Property | Type | Description |
---|---|---|
path |
string | The Osc path to use for this parameter. |
type |
i for Integer, f for Float |
The destination(s) to send notifications to. |
low |
number | (Optional) The low boundary. |
high |
number | (Optional) The high boundary. |
Mappings#
The mappings between all the controls defined in the controls
section go into the mappings
section - it's an array of Mapping values, which hold the relation info between the controls. The mappings can be unidirectional for one way control or bidirectional to keep both controls in sync with one another.
Here's some example mappings, reusing control names defined in previous examples:
...
"mappings": [
[ "Digital Playback Volume", "<->", "volume_encoder" ],
[ "rec_vol", "<-", "rec_vol_right", { "chan_a": 1 } ]
]
...
The first mapping is a bidirectional mapping between ALSA mixer's Digital Playback Volume
and Pisound Micro volume_encoder
control. The ALSA mixer controls are stereo, such mappings by default assign both channels to the single control. The 2nd mapping is a one directional mapping from osc's rec_vol_right
control to ALSA mixer's Digital Capture Volume
, referred to via the rec_vol
alias, this mapping will affect only the right channel.
Mapping Type#
The Mapping type is an array of either 3 or 4 members:
Member | Name | Type | Description |
---|---|---|---|
1st | Control A | string | The name of the first control. |
2nd | Direction | Direction enum | The mapping direction. |
3rd | Control B | string | The name of the second control. |
4th | Options | Options object | (Optional) Additional options for the mapping. |
Direction Enum#
Enum Entry | Description |
---|---|
-> |
Map from A to B. |
<- |
Map from B to A. |
<-> |
Map in both directions. |
A mapping "from A to B" means that whenever you move the A control, the B control will get adjusted accordingly.
Options Object#
Property | Description |
---|---|
chan_a |
(Optional) The channel to use for control A, default is to use all. |
chan_b |
(Optional) The channel to use for control B, default is to use all. |