npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@seamless-medley/medley

v0.4.6

Published

Audio engine for Node.js, with built-in "radio like" gapless/seamless playback

Downloads

72

Readme

node-medley

NPM Version node-medley native module

node-medley is a Node.js native module built on top of JUCE framework. It provides audio playback to both audio output device and Node.js stream

Features

  • Cross-platform
  • Seamless playback with automatic track transitions
  • Smooth track transition, with customizable transition points
  • Track metadata reading, including cover art and lyrics
  • ReplayGain support
  • Audio level normalization (in conjunction with ReplayGain)
  • Built-in audio limiter
  • Built-in Karaoke effect (vocal removal)
  • Audio level measurement
  • Play directly to audio device
  • Consumption of PCM data via Node.js streams

Supported platforms

  • linux/amd64
  • linux/arm64
  • win32/x64
  • macOS/x64
  • macOS/arm64

Table of contents

Installation

With npm:

npm i @seamless-medley/medley

With pnpm:

pnpm add @seamless-medley/medley

Getting started

// Import the main classes
import { Medley, Queue } from '@seamless-medley/medley';

// Create a queue instance and pass it to the Medley class during instantiation
const queue = new Queue();
const medley = new Medley(queue);

// Add tracks to the `queue` and start playing
queue.add('/path/to/file');
queue.add('/path/to/file2');
medley.play();

This will start playback on the default audio device.

Supported File Formats

Currently, the supported file formats include: wav, aiff, mp3, ogg and flac. More formats may be added in the future.

Guide

Getting available audio devices

Simply use the getAvailableDevices method.

Selecting audio device

Utilize data returned from the getAvailableDevices method with the setAudioDevice method.

Example:

// Use the default device of the first type

const allDevices = medley.getAvailableDevices();
medley.setAudioDevice({
    type: allDevices[0].type,
    device: allDevices[0].defaultDevice
});

Null Audio device

node-medley includes a special audio device called Null Device which does not output sound to a physical audio device.

This is useful in environments without installed audio devices or when only PCM audio data consumption is needed, see requestAudioStream method.

Example:

medley.setAudioDevice({ type: 'Null', device: 'Null Device' });

Getting PCM data

// Request a stream of signed 16-bit, little endian audio at 48000 sample rate
const result = await medley.requestAudioStream({
    format: 'Int16LE',
    sampleRate: 48000
});

// Pipe to another stream
result.stream.pipe(/* destination */);

// Or intercept data with the `data` event
result.stream.on('data', (buffer) => {
    // Do something with `buffer`
});

// When finished, don't forget the delete the stream
medley.deleteAudioStream(result.id);

Dynamic queue

Sometimes, preloading all tracks can lead to monotony, so the queue can dynamically update using the enqueueNext event

Example:

medley.on('enqueueNext', (done) => {
    const newTrack = getNewFreshTrack(); // Logic for fetching new track
    queue.add(newTrack);
    done(true);
});

Check if a track is loadable

Use isTrackLoadable static method.

Getting metadata

Metadata can be retrieved in two ways:

  1. From a file path
  2. From the deck

Getting cover art and lyrics

Utilize the getCoverAndLyrics method.

Reading audio level information

Real-time audio level are accessible via the level property.

Example:

// Read audio levels at a rate of 30 times per second
setInterval(() => {
    const audioLevel = medley.level;
    // Use the value returned
}, 1000 / 30);

Normalizing tracks audio level

ReplayGain analyzes and adjusts perceived loudness.

node-medley supports reading ReplayGain Track-gain metadata embeded in audio files.

To embed it, use one of these scanners.

Usually, ReplayGain attenuates the played back audio, a make-up gain should be applied to boost the audio level back to the normalized level, you can change this make-up gain by changing the replayGainBoost property.

The make-up gain will not cause clipping, because there is an audio limiter preventing that from happening in the audio pipline.

Custom transition point

node-medley automatically analyzes track to find audio positions in which it should start/stop playing and also the positions/durations the transition between track should occur.

but, sometimes this may not be as intended, you can customize that by giving node-medley some hints.

The hints can come from the metadata embed in the track itself using user-defined tag, here are the supported tags:

  • CUE-IN or CUE_IN - Start position of the track, in seconds

    This correspond to the TrackInfo cueInPosition property.

  • CUE-OUT or CUE_OUT - Stop position of the track, in seconds

    This correspond to the TrackInfo cueOutPosition property.

Alternatively, you can provide that values when adding a track into the queue.

See also:

API

Medley class

This is the main class, the constructor accepts an instance of the Queue class.

new Medley(queue, options)

NOTE: JavaScript Array cannot be used as a queue.

Options?

  • logging (boolean?) - Enable logging, See log event
  • skipDeviceScanning (boolean?) - Skip scanning for audio devices

Methods

play(shouldFade = true)

Start playing, if the playing was previously paused it will be resumed.

The shouldFade parameter is used only when resuming.

stop(shouldFade = true)

Stop playing.

togglePause(shouldFade = true)

Toggle play/pause.

fadeOut()

Forcefully transit to the next track with a fade-out effect.

seek(time, deckIndex?)

  • time (number) - Time in seconds.
  • deckIndex (number?) - Deck index, possible values are: 0, 1, 2

seekFractional(fraction, deckIndex?)

  • fraction (number) - Fraction of the track's length.

    • 0 - Seek to the beginning.
    • 0.5 Seek to the middle of the track.
  • deckIndex optional deck index, possible values are: 0, 1, 2

getDeckPositions(deckIndex)

  • deckIndex (number) - Deck index, possible values are: 0, 1, 2

Returns an object with:

  • current (number?) - Current playing position
  • duration (number?) - Total duration
  • first (number?) - First audible position
  • last (number?) - Last audible position
  • leading (number?) - Fade-in position
  • trailing (number?) - Fade-out position
  • cuePoint (number?)
  • transitionStart (number?)
  • transitionEnd (number?)

getDeckMetadata(deckIndex)

  • deckIndex (number) - Deck index, possible values are: 0, 1, 2

Returns Metadata for the specified deckIndex

getAvailableDevices()

Returns array of object describing audio devices.

  • type (string) - Device type
  • isCurrent (boolean) - true if this device type is currently selected
  • devices (string[]) - List of devices of this type
  • defaultDevice (string) - Default device name of this type
  • currentDevice (string | undefined) - Currently selected device name of this type, undefined if none

getAudioDevice()

Get the audio device currently being selected, returns undefined if none.

If available, returns an object with:

  • type (string) - Device type
  • device (string) - Device name

setAudioDevice(descriptor)

Set audio device used for playback.

The descriptor is an object containing:

  • type (string?) - Device type, if omitted, the currently selected device type is used
  • device (string?) - Device name, if omitted, the default

If both fields are omitted, this method does nothing.

Returns false if the specified device cannot be used.

Returns true if some device is selected.

Use getAudioDevice() to get the actual selected device.

requestAudioStream(options?)

Request a PCM audio data stream

options? is an object with:

  • sampleRate (number) - Sample rate for the PCM data. Defaults to the default device's sample rate if omitted.

  • format - Audio sample format, possible values are:

    • Int16LE - 16 bit signed integer, little endian
    • Int16BE - 16 bit signed integer, big endian
    • FloatLE - 32 bit floating point, little endian
    • FloatBE - 32 bit floating point, big endian
  • bufferSize (number) - Maximun frames the internal buffer can hold, increase this value helps reduce stuttering in some situations

    • Default value is 250ms (deviceSampleRate * 0.25)
  • buffering (number):

    • Number of frames to buffer before returning the buffered frames back to Node.js stream
    • Reducing this value will cause the stream to pump faster
    • Default value is 10ms (deviceSampleRate * 0.01)
  • gain (number) - Output gain, a floating point number ranging from 0 to 1

  • fx (object) - Effects parameter:

Returns a Promise of object with:

  • id (number) - The request id, use this value to update or delete the requested stream

  • channels (number) - Number of audio channels, This is usuaully 2

  • originalSampleRate (number) - Original sample rate in audio pipeline

  • sampleRate (number) - Sample rate as requested

  • bitPerSample (number) - Bit per sample, depending on the format

    • 16 - for Int16LE of Int16BE
    • 32 - for FloatLE of FloatBE
  • stream (Readable) - Readable stream, for consuming PCM data

  • update ((options) => boolean) - Update this audio stream, the options is the same as updateAudioStream(id, options)

  • getLatency () => number - Get the audio latency

  • getFx - See getFx

    Calling this method from this object only effects the corresponding stream, but does not effect the main output.

  • setFx - See setFx

    Calling this method from this object only effects the corresponding stream, but does not effect the main output.

updateAudioStream(id, options)

Update the requested audio stream specified by id returned from requestAudioStream method.

options is an object with:

Returns true if succeeded.

deleteAudioStream(id)

Delete the requested audio stream specified by id returned from requestAudioStream method.

getFx(type: 'karaoke')

Get all parameter values for the karaoke effect.

Returns an object with:

  • enabled (boolean)

  • mix (number) - Dry/Wet for the karaoke effect

  • lowpassCutoff (number) - Cut off frequency for the low pass filter

  • lowpassQ (number) - Quality factor for the low pass filter

  • highpassCutoff (number) - Cut off frequency for the high pass filter

  • highpassQ (number) - Quality factor for the high pass filter

setFx(type: 'karaoke', params)

Set karaoke effect parameters.

The params is an object with:

  • enabled (boolean?)

  • dontTransit (boolean?) - Do not apply dry/wet mix transition while enabling/disabling the effect. Must be used with the enabled property.

  • mix (number?) - Dry/Wet for the karaoke effect

  • lowpassCutoff (number?) - Cut off frequency for the low pass filter

  • lowpassQ (number?) - Quality factor for the low pass filter

  • highpassCutoff (number?) - Cut off frequency for the high pass filter

  • highpassQ (number?) - Quality factor for the high pass filter

Returns true if succeeded.

Properties

playing

Type: boolean

Read only

Returns true if playing, but not affected by the paused property.

paused

Type: boolean

Read only

Returns true if playing but has been paused.

volume

Type: number

Audio volume in linear scale.

0 = Silent

1 = 0dBFS

fadingCurve

Type: number

Minimum: 0

Maximum: 100

S-Curve value used for fading in/out.

maximumFadeOutDuration

Type: number

Maximum duration in seconds for the fade-out transition between tracks.

minimumLeadingToFade

Type: number

Duration in seconds at the beginning of a track to be considered as having a long intro.

A track with a long intro will cause a fading-in to occur during transition.

replayGainBoost

Type: number

Default: 9.0

Gain (in dB) to boost for tracks with ReplayGain metadata embeded, default to 9.0dB.

If a track has no ReplayGain metadata, this value is ignored.

level

Read only

Returns an object with:

  • left - Left channel level
  • right - Right channel level

With each channel having:

  • magnitude (number) - Audio level

  • peak (number) - Holding peak

reduction

Read only

Returns audio reduction level in dB

Audio reduction occur during the internal audio processing

Events

Deck events

Parameters:

  • deckIndex (number) - Deck index where the event occur

  • trackPlay - An object describing detail of the play session for the Deck.

    • uuid (string) - A unique string identifying the trackPlay itself
    • track - Track, see TrackInfo
    • duration (number) - Track duration

loaded

Emits when a track has been loaded into a Deck.

unloaded

Emits when a track has been unloaded from a Deck.

started

Emits when a Deck starts playing.

finished

Emits when a Deck finishes playing.

mainDeckChanged

Emits when a Deck becomes the main playing Deck.

enqueueNext(done)

Emits when the playing queue is exhausted and needs to be filled.

See Dynamic quque

Parameter:

  • done - Call this function in the event handler with true value to inform node-medley that at least a track has been added to the queue and should be loaded.

audioDeviceChanged

Emits when the audio device changes, use getAudioDevice method to get the current audio device.

log

Emits when a log message is pushed from the native module.

Logging must be enabled when constructing the Medley instance, see Medley class options

Parameter:

  • level (number) - Log level

    | name |value| |------|-----| |trace | -1 | |debug | 0 | |info | 1 | |warn | 2 | |error | 3 | |fatal | 4 |

  • name (string) - The logger's name

  • msg (string) - Log message

Static methods

getInfo

Returns an object containing information about node-medley

  • runtime:

    • file - Node native module file name
    • runtime - Runtime name
    • napi - node-addon-api version
  • version - node-medley version number

  • juce - Detail for the JUCE framework library being linked into node-medley

    • version
    • cpu
      • intel - Intel CPU
      • arm - ARM CPU
      • arm64 - ARM64 CPU
      • aarch64 - ARM64 CPU
      • sse - SIMD supports on x84_64 CPU
      • neon - SIMD supports on ARM CPU
      • vdsp - vDSP supports on macOS

isTrackLoadable(track)

Returns true if the track can be loaded and played.

getMetadata(path)

Returns Metadata for path

getAudioProperties(path)

Returns AudioProperties for path

Please note that this function may scan the whole file in order to get a good result.

getCoverAndLyrics(path)

Returns an object with:

  • cover (Buffer) - Cover art data

  • coverMimeType (string) - Cover art MIME type

  • lyrics (string) - Raw lyrics data

Queue class

The queue class provides a list of tracks to the Medley class.

Constructor

new Queue(tracks?)

Create a new instance of the Queue class, an optional tracks is an array of tracks to initially fill the queue.

The Queue class is straightforward, For more control over your tracks list, manage it manually and provide a track when the Medley object requires one, see enqueueNext event

Methods

add(track)

Add a track to the queue, see TrackInfo

add(tracks)

Add a list of tracks to the queue, see TrackInfo

insert(index, track)

insert(index, tracks)

Insert track(s) at position specified by the index parameter.

delete(index, count = 1)

Delete tracks(s) specified by count starting from index.

swap(index1, index2)

Swap tracks.

move(currentIndex, newIndex)

Move a track from currentIndex to newIndex.

get(index)

Get the track at index

set(index, track)

Set the track at index

toArray()

Returns a new shallow copy of all tracks.

Properties

length property

Total number of tracks in the queue.

TrackInfo

A TrackInfo can be either a string to file path, or an object with:

  • path (string) - File path

  • cueInPosition (number?) - Start position of the track

  • cueOutPosition (number?) - Stop position of the track

  • disableNextLeadIn (boolean?)

    • Disables the lead-in of the next track, useful for transitioning from jingles/sweepers.
    • The lead-in is the position where it is considered as the start singing point, usually presented in a track which has smooth/long beginning.

Metadata

  • title (string?)
  • artist (string?)
  • album (string?)
  • isrc (string?) - International Standard Recording Code
  • albumArtist (string?)
  • originalArtist (string?)
  • trackGain (number?) - ReplayGain value in dB (decibels), 0 means no ReplayGain value for this track
  • bpm (number?) - Beats Per Minute
  • comments ([string, string][]) - List of key/value pairs for all user-defined comments

AudioProperties

  • bitrate (number?) - in Kbps
  • sampleRate (number?) - in Hz
  • duration (number?) - in seconds

Demo musics

Demo music from Bensound.com