UNPKG

pw-client

Version:

Node.js wrapper for developing PipeWire clients

378 lines (334 loc) 9.83 kB
import { endianness } from "node:os"; import type { TypedNumericArray, TypedNumericArrayCtor, } from "./audio-output-stream.mjs"; export type OutputBufferFactory = (numSamples: number) => OutputBuffer; export interface OutputBuffer { get buffer(): ArrayBuffer; set(index: number, signedFloat: number): void; subarray(index: number, sampleCount: number): OutputBuffer; } function fpOutputBuffer(ctor: TypedNumericArrayCtor): OutputBufferFactory { class WrappedTypeArray { #buffer: TypedNumericArray; constructor(buffer: TypedNumericArray) { this.#buffer = buffer; } set(index: number, value: number) { this.#buffer[index] = value; } get buffer() { return this.#buffer.buffer; } subarray(offset: number, size: number) { return new WrappedTypeArray(this.#buffer.subarray(offset, size)); } } return (samples: number) => new WrappedTypeArray(new ctor(samples)); } function intOutputBuffer( ctor: TypedNumericArrayCtor, encode: (fpSigned: number) => number ): OutputBufferFactory { class WrappedTypeArray { #buffer: TypedNumericArray; constructor(buffer: TypedNumericArray) { this.#buffer = buffer; } set(index: number, value: number) { const encoded = encode(value); this.#buffer[index] = encoded; } get buffer() { return this.#buffer.buffer; } subarray(offset: number, size: number) { return new WrappedTypeArray(this.#buffer.subarray(offset, size)); } } return (samples: number) => new WrappedTypeArray(new ctor(samples)); } function signedInt(bits: number) { const multiplier = 2 ** (bits - 1) - 0.5; return (n: number) => Math.floor(n * multiplier); } function unsignedInt(bits: number) { const multiplier = (2 ** bits - 1) / 2; return (n: number) => Math.floor((n + 1) * multiplier); } const Int8Buffer = intOutputBuffer(Int8Array, signedInt(8)); const Int16Buffer = intOutputBuffer(Int16Array, signedInt(16)); const Int32Buffer = intOutputBuffer(Int32Array, signedInt(32)); const Uint8Buffer = intOutputBuffer(Uint8Array, unsignedInt(8)); const Uint16Buffer = intOutputBuffer(Uint16Array, unsignedInt(16)); const Uint32Buffer = intOutputBuffer(Uint32Array, unsignedInt(32)); const Float32Buffer = fpOutputBuffer(Float32Array); const Float64Buffer = fpOutputBuffer(Float64Array); /** * Audio format class representing different sample formats supported by PipeWire. * Handles conversion between JavaScript Numbers and various binary audio formats. * * Users typically don't need to work with AudioFormat directly - it's handled * internally through quality presets and format negotiation. * * @class AudioFormat * * @example * ```typescript * // Access negotiated format info * console.log(`Stream format: ${stream.format.description}`); * console.log(`Sample rate: ${stream.rate}Hz`); * console.log(`Channels: ${stream.channels}`); * ``` */ export class AudioFormat { readonly #enumValue: number; readonly #byteSize: number; readonly #bufferFactory: OutputBufferFactory; readonly #description: string; private constructor( value: number, byteSize: number, BufferClass: OutputBufferFactory, description: string ) { this.#enumValue = value; this.#byteSize = byteSize; this.#bufferFactory = BufferClass; this.#description = description; AudioFormat.#enumMap.set(value, this); } get enumValue() { return this.#enumValue; } get byteSize() { return this.#byteSize; } get BufferClass() { return this.#bufferFactory; } get description() { return this.#description; } static readonly #enumMap = new Map<number, AudioFormat>(); static fromEnum(format: number) { return AudioFormat.#enumMap.get(format); } static Int8 = new AudioFormat( 0x101, 1, Int8Buffer, "8-bit signed integer (low quality)" ); static Uint8 = new AudioFormat( 0x102, 1, Uint8Buffer, "8-bit unsigned integer (basic quality)" ); static get Int16() { return endianness() === "BE" ? AudioFormat.Int16BE : AudioFormat.Int16LE; } static get Uint16() { return endianness() === "BE" ? AudioFormat.Uint16BE : AudioFormat.Uint16LE; } static get Int24_32() { return endianness() === "BE" ? AudioFormat.Int24_32BE : AudioFormat.Int24_32LE; } static get Uint24_32() { return endianness() === "BE" ? AudioFormat.Uint24_32BE : AudioFormat.Uint24_32LE; } static get Int32() { return endianness() === "BE" ? AudioFormat.Int32BE : AudioFormat.Int32LE; } static get Uint32() { return endianness() === "BE" ? AudioFormat.Uint32BE : AudioFormat.Uint32LE; } // static get Int24() { // return endianness() === "BE" ? AudioFormat.Int24BE : AudioFormat.Int24LE; // } // static get Uint24() { // return endianness() === "BE" ? AudioFormat.Uint24BE : AudioFormat.Uint24LE; // } // static get Int20() { // return endianness() === "BE" ? AudioFormat.Int20BE : AudioFormat.Int20LE; // } // static get Uint20() { // return endianness() === "BE" ? AudioFormat.Uint20BE : AudioFormat.Uint20LE; // } // static get Int18() { // return endianness() === "BE" ? AudioFormat.Int18BE : AudioFormat.Int18LE; // } // static get Uint18() { // return endianness() === "BE" ? AudioFormat.Uint18BE : AudioFormat.Uint18LE; // } static get Float32() { return endianness() === "BE" ? AudioFormat.Float32BE : AudioFormat.Float32LE; } static get Float64() { return endianness() === "BE" ? AudioFormat.Float64BE : AudioFormat.Float64LE; } static ULaw = new AudioFormat(0x11f, 1, Int8Buffer, "μ-law compressed audio"); static ALaw = new AudioFormat(0x120, 1, Int8Buffer, "A-law compressed audio"); static Uint8Planar = new AudioFormat( 0x201, 1, Uint8Buffer, "8-bit unsigned planar" ); static Int16Planar = new AudioFormat( 0x202, 2, Int16Buffer, "16-bit signed planar" ); static Int24_32Planar = new AudioFormat( 0x203, 4, Int32Buffer, "24-bit in 32-bit planar" ); static Int32Planar = new AudioFormat( 0x204, 4, Int32Buffer, "32-bit signed planar" ); // static Int24Planar = new AudioFormat(0x205, 3); static Float32Planar = new AudioFormat( 0x206, 4, Float32Buffer, "32-bit floating point planar" ); static Float64Planar = new AudioFormat( 0x207, 8, Float64Buffer, "64-bit floating point planar" ); static Int8Planar = new AudioFormat( 0x208, 1, Int8Buffer, "8-bit signed planar" ); // Endian-specific private static readonly Int16LE = new AudioFormat( 0x103, 2, Int16Buffer, "16-bit signed integer (standard quality)" ); private static readonly Int16BE = new AudioFormat( 0x104, 2, Int16Buffer, "16-bit signed integer (standard quality)" ); private static readonly Uint16LE = new AudioFormat( 0x105, 2, Uint16Buffer, "16-bit unsigned integer (standard quality)" ); private static readonly Uint16BE = new AudioFormat( 0x106, 2, Uint16Buffer, "16-bit unsigned integer (standard quality)" ); private static readonly Int24_32LE = new AudioFormat( 0x107, 4, Int32Buffer, "24-bit in 32-bit container (professional quality)" ); private static readonly Int24_32BE = new AudioFormat( 0x108, 4, Int32Buffer, "24-bit in 32-bit container (professional quality)" ); private static readonly Uint24_32LE = new AudioFormat( 0x109, 4, Uint32Buffer, "24-bit unsigned in 32-bit container (professional quality)" ); private static readonly Uint24_32BE = new AudioFormat( 0x10a, 4, Uint32Buffer, "24-bit unsigned in 32-bit container (professional quality)" ); private static readonly Int32LE = new AudioFormat( 0x10b, 4, Int32Buffer, "32-bit signed integer (high precision)" ); private static readonly Int32BE = new AudioFormat( 0x10c, 4, Int32Buffer, "32-bit signed integer (high precision)" ); private static readonly Uint32LE = new AudioFormat( 0x10d, 4, Uint32Buffer, "32-bit unsigned integer (high precision)" ); private static readonly Uint32BE = new AudioFormat( 0x10e, 4, Uint32Buffer, "32-bit unsigned integer (high precision)" ); // private static Int24LE = new AudioFormat(0x10f, 3); // private static Int24BE = new AudioFormat(0x110, 3); // private static Uint24LE = new AudioFormat(0x111, 3); // private static Uint24BE = new AudioFormat(0x112, 3); // private static Int20LE = new AudioFormat(0x113, 4); // No idea how to encode... // private static Int20BE = new AudioFormat(0x114, 4); // No idea... // private static Uint20LE = new AudioFormat(0x115, 4); // No idea... // private static Uint20BE = new AudioFormat(0x116, 4); // No idea... // private static Int18LE = new AudioFormat(0x117, 3); // No idea... // private static Int18BE = new AudioFormat(0x118, 3); // No idea... // private static Uint18LE = new AudioFormat(0x119, 3); // No idea... // private static Uint18BE = new AudioFormat(0x11a, 3); // No idea... private static readonly Float32LE = new AudioFormat( 0x11b, 4, Float32Buffer, "32-bit floating point (excellent quality)" ); private static readonly Float32BE = new AudioFormat( 0x11c, 4, Float32Buffer, "32-bit floating point (excellent quality)" ); private static readonly Float64LE = new AudioFormat( 0x11d, 8, Float64Buffer, "64-bit floating point (highest precision)" ); private static readonly Float64BE = new AudioFormat( 0x11e, 8, Float64Buffer, "64-bit floating point (highest precision)" ); }