Skip to content

Commit 6ed8bea

Browse files
authored
Merge pull request #81 from timothyjward/alsa-contribution
Document the ALSA contribution model in Volumio 3.x
2 parents bdd6451 + c04ce9c commit 6ed8bea

File tree

2 files changed

+86
-1
lines changed

2 files changed

+86
-1
lines changed

docs/03_Plugin_System/03_Index.js.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,18 @@ IMPORTANT TIPS:
4848

4949
Then we add all the required functions for a generic plugin:
5050

51+
### Plugin Lifecycle
52+
53+
With the release of Volumio 3, the lifecycle of Volumio plugins has been better defined. Plugins are expected to complete `onVolumioStart` and `onStart` asynchronously by returning a Promise which completes when they have finished starting. Volumio will wait for all plugins to finish `onVolumioStart` before calling `onStart` on any plugin. Only once `onStart` has completed for all plugins will Volumio have finished starting.
54+
55+
Note that plugin startup speed does affect the overall Volumio startup time. Try to avoid having long-running tasks that block startup. If a plugin appears to be *stuck*, and doesn't resolve the promise that it returns from `onVolumioStart` or `onStart` then Volumio will regard this plugin as having failed to start.
56+
57+
One key difference between `onVolumioStart` and `onStart` is that it is *not safe* to play audio during `onVolumioStart`. While `onVolumioStart` is running plugins may still be making changes or creating resources that are needed by the audio stack. It is also considered good manners not to rely on functions (other than reading the configuration) from other plugins in `onVolumioStart`. This limits the likelihood that a plugin that you write will call into another plugin before it is fully initialised.
58+
5159

5260
#### On Volumio Start
5361

54-
This is the code that gets executed when Volumio starts and triggers the plugin start. Typically, what you do is load the plugin configuration.
62+
This is the code that gets executed when Volumio starts and triggers the plugin start. Typically, what you do is load the plugin configuration, set up data structures, and create any necessary resources
5563

5664
```javascript
5765
ControllerSpop.prototype.onVolumioStart = function()
@@ -60,8 +68,14 @@ ControllerSpop.prototype.onVolumioStart = function()
6068
this.config = new (require('v-conf'))();
6169
this.config.loadFile(configFile);
6270

71+
var promise = libQ.nfcall(fs.writeFile, '/tmp/message', 'Hello World', 'utf8');
72+
73+
return promise;
6374
}
6475
```
76+
**IMPORTANT:**
77+
78+
* You'll notice that we use promises here. That's why Volumio needs to know when the plugin has actually started, or if it failed. So what we're doing is returning the promise on successful start, and rejecting it if it doesn't start properly.
6579

6680
#### On Start
6781

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
## Custom ALSA Contributions
2+
3+
Starting with Volumio 3 plugins are forbidden from directly changing the ALSA configuration in `/etc/asound.conf`. This is to resolve the many issues that occurred when multiple plugins wanted to make changes to the ALSA configuration, and to simplify the behaviour of sofware volume control.
4+
5+
Clearly some plugins will need to customise the Volumio ALSA configuration to provide their functionality. The way that this is achieved is through ALSA contribution snippets.
6+
7+
### What is an ALSA Contribution Snippet
8+
9+
An ALSA contribution snippet is a piece of ALSA configuration provided by a plugin in a specially named file. It is designed to be easily assembled (along with other contribution snippets) into a complete ALSA configuration.
10+
11+
The simplest valid ALSA contribution snippet would be a file named `in.out.conf` with the following content:
12+
13+
```
14+
pcm.in {
15+
type copy
16+
slave.pcm "out"
17+
}
18+
```
19+
20+
There are several important things to note about this snippet:
21+
* The file name has is of the form `<a>.<b>.conf`, where `<a>` and `<b>` are the "input" and "output" of the contribution snippet
22+
* The snippet does not attempt to directly address the audio hardware - it only deals with the behaviour of the plugin (in this case an no-op transform)
23+
* Snippets should expect that their input samples are linear, and should output linear samples.
24+
* The snippet must be prepared to handle a variety of input sample rates, bit depths, and channel counts. In most cases this will not need special handling, however if it does then an ALSA `plug` can be used.
25+
26+
### Providing an ALSA Contribution Snippet
27+
28+
Plugins must opt-in to providing ALSA contributions. This is done by setting the `has_alsa_contribution` property to `true` in the `volumio_info` of the plugin's `package.json`.
29+
30+
```
31+
{
32+
"name": "my_plugin",
33+
"version": "1.0.0",
34+
"description": "My super cool plugin",
35+
"main": "index.js",
36+
"scripts": {
37+
"test": "echo \"Error: no test specified\" && exit 1"
38+
},
39+
"author": "Volumio",
40+
"license": "ISC",
41+
"volumio_info": {
42+
"plugin_type": "audio_interface",
43+
"boot_priority":4,
44+
"has_alsa_contribution":true
45+
}
46+
}
47+
```
48+
In addition to the `package.json` the plugin must place its ALSA contribution snippet(s) in a well defined location
49+
50+
#### Static ALSA Contribution Snippets
51+
52+
Static snippets are ones which do not rely on any configuration or runtime values, and so can be hardcoded as part of the plugin archive. Most plugins will be able to use static snippets.
53+
54+
Static snippets live inside the plugin in a folder called `asound`.
55+
56+
#### Dynamic ALSA Contribution Snippets
57+
58+
In some cases it is necessary to customise an ALSA snippet based on configuration, or data which can be found only once the plugin is installed. This obviously prevents the snippet from being included as part of the plugin archive.
59+
60+
Dynamic Contributions should be generated in `onVolumioStart` and written to the `asound` directory of plugin configuration folder. For example:
61+
62+
```
63+
var folder = self.commandRouter.pluginManager.getConfigurationFile(self.context, 'asound');
64+
var promise = libQ.nfcall(fs.writeFile, folder + '/in.out.conf', content, 'utf8');
65+
```
66+
67+
### ALSA Contribution Snippet Ordering
68+
69+
In general plugins should avoid trying to pick a particular order for snippets to run. There are, however, some cases where it is preferable to supply an ordering hint to Volumio.
70+
71+
In these scenarios the name of the ALSA contribution can be adjusted to include a third numeric segment `<a>.<b>.<priority>.conf`. The priority is an integer, and defaults to zero if not specified. Larger (more positive) values are put closer to the audio source. Lower (more negative) values are put closer to the hardware.

0 commit comments

Comments
 (0)