-
Notifications
You must be signed in to change notification settings - Fork 3
Sample Cache
PulseAudio provides a sample cache that allows the client to upload audio files to the server where they will be stored in memory. The audio files can be played using a single command sent to the PulseAudio server. This page illustrates how a JavaScript application can upload a wav file to the sample cache and play it.
The wav module provides a convenient streaming API to wav files. Import the library, initialize a PulseAudio client and connect to the server. First, create a PulseAudio client object and connect to the server.
import * as fs from 'fs';
import * as wav from 'wav';
import { PulseAudio, PA_SAMPLE_FORMAT } from 'pulseaudio.js';
const pa = new PulseAudio();
await pa.connect();
The following JavaScript snippet shows how to upload a wav file into the sample cache on the PulseAudio server. We create a filesystem stream reader for the wav file (beep.wav) and read the entire file into memory using a reader provided by the wav module. We then create a new upload stream using the method createUploadStream
and write the data from memory to the stream. Note that error checking is omitted for brevity.
const name = 'beep.wav';
// Create a new filesystem read stream for the wav file and a new wav format reader
const file = fs.createReadStream(name);
const reader = new wav.Reader();
// The contents of the wav file will be stored in the following variable in the form
// of an array of buffers.
const data = [];
// The wav format reader emits a "format" event once it has read the entire wav header.
// We do all our processing in the callback for the event.
reader.once('format', ({ bitDepth, channels, sampleRate }) => {
// PulseAudio server needs to know the total length of each sample file. Since
// the total length isn't present in the wav header, we need to load the entire
// file into memory first.
reader.on('data', c => data.push(c));
// The following event is emitted once the entire wav file has been read
reader.once('end', async () => {
// Calculate the total length of the wav file in bytes
const maximumLength = data.reduce((a, v) => a + v.length, 0);
// Create an object describing the format of the sample file in a format
// understood by PulseAudio server API
const sampleSpec = {
format : bitDepth === 16 ? PA_SAMPLE_FORMAT.S16LE : PA_SAMPLE_FORMAT.U8,
rate : sampleRate,
channels
};
// Create a new upload stream. Use the filename as the name of the sample
const output = await pa.createUploadStream({ name, maximumLength, sampleSpec });
// Write all WAV data from the memory buffer into the upload stream and close it
for(const chunk of data) output.write(chunk);
output.end();
});
});
// Connect the file reader stream to the wav reader stream to get the upload started
file.pipe(reader);
Since the PulseAudio server needs to know the total size of the audio file in advance, the wav file cannot be simply streamed to the server. Instead, we load the entire wav file into memory and calculate its size. This is necessary because the wav file header is not guaranteed to provide the length of the file contents. The method createUploadStream
needs be provided with the name of the sample to be created, its format, and the length of data in bytes. In the above code, we calculate all the parameters from the data passed to the 'format' event callback by the wav
module.
To play the sample, invoke the method playSample
with the name of the sample as argument. The method returns a number that identifies the created playback stream (not very useful at the moment).
const num = await pa.playSample('beep.wav');
Audio samples remain in the cache on the server until removed or until the server is restarted. Use the method removeSample
to remove the audio sample manually:
await pa.removeSample('beep.wav');