Using Plugins 1 - The platform-init Plugin
In this first tutorial on soundworks plugins, we will learn why and how to use the platform-init plugin, which aims to simplify the use of certain features (such as the Web Audio API) that require a user gesture to be properly initialized.
The tutorial requires basic knowledge of the soundworks
wizard, so please refer to the getting started tutorial if you didn't check it yet.
Relevant documentation
Scaffolding the application and installing the plugin
First thing first, let's generate a new empty application with the @soundworks/create
wizard. When the wizard will ask you for the name of the default client, enter the following information:
- Name it
player
- Select the
browser
target - Select the
default
template
cd /path/to/working/directory
npx @soundworks/create@latest plugin-platform-init
Once done, go to the directory and launch the soundworks
wizard again to install the plugin:
cd plugin-platform-init
npx soundworks
Select the install / uninstall soundworks plugins
entry:
Select the @soundworks/plugin-platform-init
using the space bar:
And confirm:
After the installation has completed, you can exit the wizard by pressing Ctrl+C
or by selecting the → exit
entry.
TIP
Note that the wizard asks you if you want to install plugins when creating the application. We just followed the long path to show you how to use the wizard within the application.
INFO
Note that this specific functionality of the wizard is just a mere alias of npm install
, it is equivalent to doing:
npm install --save @soundworks/plugin-platform-init
If you open the package.json
file, you should see the @soundworks/plugin-platform-init
appearing under the dependencies
key:
// package.json
"dependencies": {
"@ircam/sc-components": "^3.0.0-alpha.44",
"@soundworks/core": "^4.0.0-alpha.0",
"@soundworks/helpers": "^1.0.0-alpha.2",
"@soundworks/plugin-platform-init": "^1.0.0-alpha.5",
"json5": "^2.2.2",
"lit": "^3.0.2"
},
INFO
Note that the actual list of dependencies and version numbers might change compared the ones listed above, as new versions of the core library and of the plugins will be released in the future.
This file allows you and the node package manager (i.e. npm
) to keep track of the dependencies of your project. Then, if you need to re-install the application dependencies (i.e. npm install
) the plugin will be re-installed as well.
Registering the plugin into soundworks
Now that the plugin is installed as a dependency of our application, let's write the code to register the plugin into soundworks.
By design, all soundworks plugins always come with both a server-side component and a client-side component. Hence, you will always have to register the plugin on both side of your application.
Server-side registration
Let's start with the server-side part and write the following code in the src/server/index.js
file. First let's import the server-side part of the plugin on top of the file:
// src/server/index.js
import '@soundworks/helpers/polyfills.js';
import { Server } from '@soundworks/core/server.js';
// import the server-side part of the `platform-init` plugin
import pluginPlatformInit from '@soundworks/plugin-platform-init/server.js';
Then register it into the server.pluginManager
component:
// src/server/index.js
const server = new Server(config);
server.useDefaultApplicationTemplate();
// register the plugin into the soundworks' plugin manager
server.pluginManager.register('platform-init', pluginPlatformInit);
The pluginManager.register
method takes at least 2 arguments:
- A user defined
id
(hereplatform-init
) that is used as an internal identifier to retrieve the plugin instance. In more advanced use-cases, it also allows you to register several times the same plugin under different ids . - A factory function (i.e.
pluginPlatformInit
) automatically executed by soundworks, from which the actual plugin instance will be created.
INFO
A factory function is common design pattern that allows to create a full-featured object from a function call. If you are interested, you can check this tutorial for more information on this subject.
And that's all for the server-side! Let's now install the plugin on the client-side.
Client-side registration
Let's open the src/clients/player/index.js
and, as for the server-side, import the client-side part of the platform-init plugin:
// src/clients/player/index.js
import { Client } from '@soundworks/core/client.js';
import launcher from '@soundworks/helpers/launcher.js';
// import the client-side part of the `platform-init` plugin
import pluginPlatformInit from '@soundworks/plugin-platform-init/client.js';
Now that our plugin is imported, we can register it into the client.PluginManager
:
// src/clients/player/index.js
const client = new Client(config);
// register the plugin into the soundworks' plugin manager
client.pluginManager.register('platform-init', pluginPlatformInit);
As for the server-side, we passed 2 arguments to the client.PluginManager#register
method: the plugin user-defined id
and the plugin factory function. An important point here is that the id
we used is the same on both server-side and client-side. This allows soundworks to automatically match and associate the two components.
At this point, if you start the server (i.e. npm run dev
) and go to http://127.0.0.1:8000, you should see the default splash screen provided by the plugin. This screen requires a user gesture (i.e. a "click" event) and is automatically shown by the launcher
because the platform-init plugin has been registered into soundworks:
Resuming an AudioContext
So far, so good but what we did until now is mainly to add a slash screen asking for a click, that does nothing particular... Or, as stated in the beginning of the tutorial, the whole aim of the platform-init
plugin is to simplify the initialization of features that require a user-gesture in order to be properly launched. Let's exemplify that by resuming an AudioContext
So let's do that.
INFO
An user gesture is required by the browsers to access or initialize certain features such as the Web Audio API, the microphone or the camera. This is a way to protect end-users against intrusive ads and/or for obvious privacy reasons.
So first, let's first create an new audioContext
:
// src/clients/player/index.js
const config = window.SOUNDWORKS_CONFIG;
// create a global audio context
const audioContext = new AudioContext();
And pass it as third argument to the pluginManager.register
method:
// src/clients/player/index.js
const client = new Client(config);
// register the plugin into the soundworks' plugin manager, and pass it the audioContext
client.pluginManager.register('platform-init', pluginPlatformInit);
client.pluginManager.register('platform-init', pluginPlatformInit, { audioContext });
INFO
By default, the platform-init plugin provides simplified access to several features, such as motion sensors, microphone and camera. Refer to the plugin documentation for more information.
Finally, let's create some welcoming sound that will show us that everything works well and that our audioContext
is properly resumed:
// src/clients/player/index.js
launcher.register(client, { initScreensContainer: $container });
// launch application
console.log(`> before start - audioContext is "${audioContext.state}"`);
await client.start();
console.log(`> after start - audioContext is "${audioContext.state}"`);
const now = audioContext.currentTime;
// create a simple envelop
const env = audioContext.createGain();
env.connect(audioContext.destination);
env.gain.setValueAtTime(0, now);
env.gain.linearRampToValueAtTime(0.5, now + 0.01);
env.gain.exponentialRampToValueAtTime(0.0001, now + 4);
const src = audioContext.createOscillator();
src.connect(env);
// randomly pick a frequency on an harmonic spectrum (150, 300, 450, etc...)
src.frequency.value = 150 + Math.floor(Math.random() * 10) * 150;
src.start(now);
src.stop(now + 4);
Reload the page (Cmd+R
) and click, you should now ear an incredibly nice sound coming from your speakers!
TIP
Note that if you emulate multiple clients in the same window (i.e. http://127.0.0.1:8000/?emulate=4), you should see how the soundworks launcher also helps you to start all the clients at once without requiring you to click on each of them:
Notes on the @soundworks/helpers
default views
The splash screen, as well as other default initialization views that we have not seen yet, are automatically created and handled the launcher
object included in the default application template:
// src/clients/player.index.js
launcher.register(client, { initScreensContainer: $container });
The launcher offer numerous way to adapt these views to your specific needs: e.g. to change the default styles, to adapt the language, or even to use completely different user defined views.
While won't cover these advanced use-case here, as they will be the subject of a dedicated tutorial, let's just see here the minimal changes we can make to customize the init view.
Let's then open the config/application.json
file which should look like this:
// config/application.json
{
name: 'plugin-platform-init',
author: '',
clients: {
player: {
target: 'browser',
default: true,
},
},
}
This file has been generated for you by the soundworks wizard, and the clients
entry is automatically updated each time you create a new client using the wizard.
However, the first 2 lines can be safely updated and are used by the launcher
to create the splash screen. Let's just change the name
and author
entries to see it in action:
name: 'plugin-platform-init',
name: 'Hello Plugins',
author: '',
author: 'me & myself',
And tada! If you now go to http://127.0.0.1:8000 you should now see something like:
INFO
By default, the init views try to adapt to the locale of the browser and falls back to English if the localized texts do not exist. However, as we only provide English and french versions of the texts at time of writing the tutorial, it's likely that you have seen the English version.
If you are comfortable with another language and are willing to contribute a localized version of this file, please open an issue or a pull request in the @soundworks/helpers
repository!
Conclusion
In this tutorial, you have learned the basics of soundworks plugins usage, and more precisely how to use the platform-init plugin. While this plugin is not per se very complex, it can save you a lot of (quite boring) development time by simplifying the initialization of certain browser features. Along the way, you also discovered some of the functionalities provided by the launcher
component that is automatically included in all soundworks applications created using the wizard.
In the next tutorial, we will wrap up what we have learned so far by creating a small working application where several clients can be used as distributed speakers by a central controller.