Security News
Input Validation Vulnerabilities Dominate MITRE's 2024 CWE Top 25 List
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
@mediamonks/channels
Advanced tools
Channels
is a channel based sound player for the web.
npm install @mediamonks/channels
import { Channels } from '@mediamonks/channels';
// create {name: string} list of filenames without extension
const soundFiles = ['sound1', 'sound2'].map(name => ({
name,
}));
// create a Channels instance, extension and path are required
const channelsInstance = new Channels({
soundsExtension: 'mp3',
soundsPath: 'static/audio/',
sounds: soundFiles, // not required, can be set later
});
// load files
await channelsInstance.loadSounds();
// play a sound
channelsInstance.play('sound1');
// play it on a channel
channelsInstance.createChannel('background-music');
channelsInstance.play('sound2', {channel: 'background-music'});
// the exact same through the actual channel
const myChannel = channelsInstance.createChannel('ui-sounds');
myChannel.play('sound2');
// stop a sound
const sound1 = channelsInstance.play('sound1');
sound1.stop();
// stop all sounds
channelsInstance.stopAll();
// stop all sounds on a channel
channelsInstance.stopAll({channel: 'background-music'});
myChannel.stopAll(); // or through the actual channel
// set main volume, or for a channel
channelsInstance.setVolume(0.5);
myChannel.setVolume(0.5);
Before we can do anything, an instance of Channels
has to be created. Two parameters are required: the location of the sound files, and the file extension to use:
new Channels({
soundsPath: 'location/of/your/files',
soundsExtension: 'mp3',
})
Don't create more than one
Channels
instance.
Optionally, an audioContext
can be passed in the constructor options. If omitted, one will be created in the Channels
constructor.
new Channels({
soundsPath,
soundsExtension,
audioContext: myAudioContext,
})
For React projects, there is a use-channels hook to easily work with a Channels
instance.
An AudioContext
created without user interaction (for example a click) will be in the suspended
state, in which no sound can be produced. This can happen for example if a Channels
instance is created on page landing without supplying a (non-suspended) audioContext
, since one will be created then automatically.
Creating a Channels
instance this way is fine by itself, just make sure to resume the context once on user interaction before playing any sounds.
const onClick = async () => {
await channelsInstance.resumeContext();
}
TLDR: The audioContext
that is used must have been created or resumed on user interaction.
To check whether the context is suspended:
channelsInstance.contextIsSuspended
Channels
uses the sample-manager for dealing with files, and creates an instance of it named sampleManager
.
channelsInstance.sampleManager
The easiest way to load files is to supply a list of objects with a name
property, matching the filenames without extension. The file extension has to be set when creating the Channels
object (which allows for an easy switch to different filetypes on certain clients).
// - soundfiles/sound1.mp3
// - soundfiles/sound2.mp3
const soundFiles = [{name: 'sound1'}, {name: 'sound2'}];
// list can be used when instantiating Channels
const channelsInstance = new Channels({
soundsPath: 'soundfiles/',
soundsExtension: 'mp3',
sounds: soundFiles,
})
// or can be set at a later stage
channelsInstance.sampleManager.addSamples(soundFiles);
// either way, loading can be done like so:
await channelsInstance.loadSounds();
// optionally, keep track of progress
await channelsInstance.loadSounds((progress) => {
// ...
});
The
loadSounds
method is an alias forsampleManager.loadAllSamples
For more info on how to define sound files, please refer to the sample-manager page.
When a sound has been loaded, it can be played by referring to its unique name
:
channelsInstance.play('sound');
A second argument can be passed with optional properties:
channelsInstance.play('sound', {
volume: 0.5,
channel: 'channel1',
loop: true,
fadeInTime: 2,
pan: 1, // between -1 and 1
startTimeOffset: 0, // in seconds
effects: {
preVolume: myEffectsChain,
}
});
The play function returns a reference to the playing sound, containing various methods to interact with the sound.
const sound = channelsInstance.play('sound');
sound.setVolume(0.5);
Stopping a sound can be done by calling stop()
on the playing sound reference.
const playingSound = channelsInstance.play('sound');
playingSound.stop();
Sounds can be faded out before stopping by providing a fadeOutTime
playingSound.stop({ fadeOutTime: 2 });
Channels are a way of grouping sounds that are played. They have their own volume and optional effects, and their output connects to the main output. They are completely optional and might not be needed at all, since sounds can also be played without a channel.
The reason to create a channel is to easily manage a group of sounds, for example to:
The only thing needed to create a channel is a unique name:
channelsInstance.createChannel('my-channel');
Second parameter can be used for some optional properties.
channelsInstance.createChannel('my-channel',{
type: 'monophonic',
volume: 0.5,
pan: 1,
effects: {
preVolume: myEffectsChain,
}
});
Check the Audio Effects section for more information about the
effects
option.
A reference to a channel is returned when creating it, or can be retrieved afterwards.
const myChannel = channelsInstance.createChannel('my-channel');
const myChannel = channelsInstance.getChannel('my-channel');
A Channel
can be either polyphonic or monophonic, which defines how many sounds can be played simultaneously on a channel:
monophonic
channel can play one sound at a time. When playing a sound on such a channel, all other sounds on that channel will be stoppedpolyphonic
channel has no restrictionsThe term
monophonic
is used loosely. Since sounds can fade in and out, even on a monophonic channel multiple sounds may be audible at the same time.
This type
can be set during creation. When no type
is given, the default polyphonic
is used.
channelsInstance.createChannel('monophonic-channel', {type: "monophonic"});
channelsInstance.createChannel('polyphonic-channel');
Using a monophonic channel can be very helpful when creating a background music layer where the music loop needs to be changed now and then.
There are two ways to play a sound on a channel. First of all, directly on the channelsInstance
:
channelsInstance.play('mysound', { channel: 'mychannel'});
Or, if you happen to have a reference to an actual channel:
myChannel.play('my-sound');
All options for the play()
method can still be used, except for the (in this context useless) channel
prop.
myChannel.play('sound', {
volume: 0.5,
loop: true,
fadeInTime: 2,
pan: 1,
effects: {
preVolume: myEffectsChain,
}
});
channelsInstance.stopAll({channel: 'channel-name'});
// or:
myChannel.stopAll();
// examples above will override any fade outs.
// if you do want those, set 'immediate' to false:
channelsInstance.stopAll({ channel: 'channel-name', immediate: false });
myChannel.stopAll({ immediate: false });
Channels can have default options to use when calling play()
or stop()
for sounds playing on that channel. This can be used for example to create a channel on which every sound automatically always loops when played.
These default options are the combination of the options for play()
and stop()
, without the channel.
// options for play
const sound = channelsInstance.play({
loop: true,
fadeInTime: 1,
volume: 0.5,
channel: 'my-channel',
pan: -1,
effects: {
preVolume: myEffectsChain,
}
});
// options for stop
sound.stop({
fadeOutTime: 1,
})
// all above props combined (except channel) can be used
const defaultStartStopProps = {
loop: true,
fadeInTime: 1,
volume: 0.5,
fadeOutTime: 1,
pan: -1,
effects: {
preVolume: myEffectsChain,
}
};
// can be set on creation as part of channel options
channelsInstance.createChannel('my-channel', { defaultStartStopProps });
// or can be set directly on a channel
myChannel.defaultStartStopProps = defaultStartStopProps;
Passing props to
play()
orstop()
will override the defaultStartStopProps of a channel.
Default props (in combination with a monophonic
channel) can be very helpful when creating a background music layer with music loops that need to change now and then:
const channel = channelsInstance.createChannel('background-music', {
type: 'monophonic',
defaultStartStopProps: { fadeInTime: 2, fadeOutTime: 2, loop: true },
});
// start a loop
channel.play('loop1');
// starting 2nd loop some time later, loop1 will fade out, loop2 will fade in
channel.play('loop2');
A SignalModifier
is something that allows the audio signal to be changed, for example to set the volume. These SignalModifiers
exist three places:
Everything in Channels
connects to the main output SignalModifier
, which is the final step before going to the actual output. A channel has its own SignalModifer
, which connects to the main SignalModifier
. In the following image, the SignalModifiers
are the blue blocks:
Sounds can be played either on a channel, or directly on the main output.
Sounds themselves also have a SignalModifier
:
A SignalModifier
always contains the following nodes:
GainNode
for setting volumeGainNode
for applying fadesStereoPannerNode
to pan the sound left or rightOptionally, custom effects chains can be added before or after these nodes.
The three places that have a SignalModifier
(sound, channel or main output) all have a set of methods implemented:
const myChannel = channelsInstance.getChannel('my-channel');
// on a channel
myChannel.setVolume(0.5);
myChannel.getVolume();
myChannel.mute();
myChannel.unmute();
myChannel.fadeOut(1); // time in seconds
myChannel.fadeIn(1);
myChannel.setPan(1); // value between -1 and 1
myChannel.getPan();
// all these also exist on a playing sound
const playingSound = channelsInstance.play('my-sound');
playingSound.setVolume(0.5);
// and on the library instance (to target the main output)
channelsInstance.setVolume(0.5);
Volume values should be
0
or higher. Keep in mind that going beyond1
might result in digital clipping.
When calling
mute()
thevolume
will be set to0
, with the additional effect that the previous volume value will be stored and used when callingunmute()
Pan values should be between
-1
(left) and1
(right).
There are a few things in Channels
that dispatch events. First of all: volume and pan changes on either a channel, a sound or the main instance.
// listening to main volume changes
channelsInstance.addEventListener('VOLUME_CHANGE', (event) => {
console.log(event.data.volume);
})
// or a channel
channel.addEventListener('VOLUME_CHANGE', (event) => {
console.log(event.data.volume);
})
// or a playing sound
playingSound.addEventListener('VOLUME_CHANGE', (event) => {
console.log(event.data.volume);
})
// all these three also dispatch pan changes
channel.addEventListener('PAN_CHANGE', (event) => {
console.log(event.data.pan);
})
When using React, there are some hooks available to work with volume or pan changes.
Apart from that, the main instance notifies when the list of channels or playing sounds updates:
channelsInstance.addEventListener('CHANNELS_CHANGE', () => {
console.log(channelsInstance.getChannels());
})
channelsInstance.addEventListener('PLAYING_SOUNDS_CHANGE', () => {
console.log(channelsInstance.getPlayingSounds());
})
Effects can be placed in the chain of a SignalModifier
and are therefor available on the three places that have a SignalModifier
:
In all of these cases, effects can be inserted before and/or after the volume is applied.
Effects have to be defined as an EffectsChain
, which is an object with an input
and an output
(both of type AudioNode
). An effect can be a single node (with input
and output
pointing to the same AudioNode
), or a long chain or multiple nodes.
Once an EffectsChain
has been created, it can be used in the preVolume
or postVolume
prop of the effects
prop.
const filter = audioContext.createBiquadFilter();
const myEffectsChain = {
input: filter,
output: filter,
}
// setting an effect on the main output, before the volume
const channelsInstance = new Channels({
soundsExtension,
soundsPath,
effects: {
preVolume: myEffectsChain
},
});
// setting it on a channel, after the volume
channelsInstance.createChannel('effect-channel', {
effects: {
postVolume: myEffectsChain,
}
})
// on a sound, before and after the volume
channelsInstance.play('my-sound', {
effects: {
preVolume: myEffectsChain,
postVolume: myOtherEffectsChain,
}
})
Both input and output use the in/out with index
0
to connect, which will work for nearly all cases. If for some reason you need to use a different index, you can add aGainNode
before/after your effects as a solution.
It is possible to route the audio from an <audio>
or <video>
element into Channels
, for example to apply effects or to control their volume along with other sounds.
To do so, use the connectMediaElement()
method on either a channel or the main instance:
// connect output to a channel
myChannel.connectMediaElement(myVideoElement);
// or to the main output
channelsInstance.connectMediaElement(myVideoElement);
FAQs
Channel-based sound player
The npm package @mediamonks/channels receives a total of 310 weekly downloads. As such, @mediamonks/channels popularity was classified as not popular.
We found that @mediamonks/channels demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.
Research
Security News
A threat actor's playbook for exploiting the npm ecosystem was exposed on the dark web, detailing how to build a blockchain-powered botnet.