UNPKG

vban

Version:
4 lines 80 kB
{ "version": 3, "sources": ["../../src/packets/VBANAudioPacket/EBitsResolutions.ts", "../../src/packets/VBANAudioPacket/ECodecs.ts", "../../src/packets/VBANPacket.ts", "../../src/packets/ESubProtocol.ts", "../../src/commons.ts", "../../src/packets/VBANSpecs.ts", "../../src/packets/VBANAudioPacket/VBANAudioPacket.ts", "../../src/packets/VBANSerialPacket/ESerialStreamType.ts", "../../src/packets/VBANSerialPacket/VBANSerialPacket.ts", "../../src/packets/VBANServicePacket/EServiceFunction.ts", "../../src/packets/VBANServicePacket/EServicePINGApplicationType.ts", "../../src/packets/VBANServicePacket/EServicePINGFeatures.ts", "../../src/packets/VBANServicePacket/EServiceType.ts", "../../src/packets/VBANServicePacket/VBANServicePacket.ts", "../../src/packets/VBANServicePacket/VBANChatPacket.ts", "../../src/packets/VBANServicePacket/VBANPingPacket.ts", "../../src/packets/VBANServicePacket/VBANRealTimePacket.ts", "../../src/packets/VBANServicePacket/VBANRealTimeRegisterAnswerPacket.ts", "../../src/packets/VBANServicePacket/VBANRealTimeRegisterPacket.ts", "../../src/packets/VBANServicePacket/VBANServicePacketFactory.ts", "../../src/packets/VBANTXTPacket/ETextEncoding.ts", "../../src/packets/VBANTXTPacket/VBANTEXTPacket.ts", "../../src/VBANProtocolFactory.ts", "../../src/VBANServer.ts", "../../src/pkg.ts"], "sourcesContent": ["//Bit Resolution\nexport enum EBitsResolutions {\n VBAN_DATATYPE_BYTE8 = 0x00,\n VBAN_DATATYPE_INT16 = 0x01,\n VBAN_DATATYPE_INT24 = 0x02,\n VBAN_DATATYPE_INT32 = 0x03,\n VBAN_DATATYPE_FLOAT32 = 0x04,\n VBAN_DATATYPE_FLOAT64 = 0x05,\n VBAN_DATATYPE_12BITS = 0x06,\n VBAN_DATATYPE_10BITS = 0x07\n}\n", "export enum ECodecs {\n VBAN_CODEC_PCM = 0x00,\n VBAN_CODEC_VBCA = 0x10, //VB-AUDIO AOIP CODEC\n VBAN_CODEC_VBCV = 0x20, //VB-AUDIO VOIP CODEC\n VBAN_CODEC_UNDEFINED_1 = 0x30,\n VBAN_CODEC_UNDEFINED_2 = 0x40,\n VBAN_CODEC_UNDEFINED_3 = 0x50,\n VBAN_CODEC_UNDEFINED_4 = 0x60,\n VBAN_CODEC_UNDEFINED_5 = 0x70,\n VBAN_CODEC_UNDEFINED_6 = 0x80,\n VBAN_CODEC_UNDEFINED_7 = 0x90,\n VBAN_CODEC_UNDEFINED_8 = 0xa0,\n VBAN_CODEC_UNDEFINED_9 = 0xb0,\n VBAN_CODEC_UNDEFINED_10 = 0xc0,\n VBAN_CODEC_UNDEFINED_11 = 0xd0,\n VBAN_CODEC_UNDEFINED_12 = 0xe0,\n VBAN_CODEC_USER = 0xf0\n}\n", "import { Buffer } from 'buffer';\nimport { ESubProtocol } from './ESubProtocol.js';\nimport { IVBANHeaderCommon } from './IVBANHeaderCommon.js';\nimport { cleanPacketString, PACKET_IDENTIFICATION, sampleRates, STREAM_NAME_LENGTH } from '../commons.js';\nimport { IVBANHeader } from './IVBANHeader.js';\nimport { VBAN_DATA_MAX_SIZE } from './VBANSpecs.js';\n\nexport class VBANPacket {\n /**\n * the subProtocol of this packet\n * {@link ESubProtocol}\n */\n public subProtocol: ESubProtocol = ESubProtocol.AUDIO;\n /**\n * the name of the current stream .\n * Voicemeeter rely on it to allow a packet or not\n */\n public streamName: string;\n /**\n * Sample Rate for this stream\n */\n public sr: number;\n /**\n * frameCounter allow checking if you receive frame in order, and without losing them\n */\n public frameCounter: number;\n\n public static readonly frameCounters: Map<string, number> = new Map<string, number>();\n\n /**\n * Extract headers and data from UDPPacket, each Packet will continue the process\n */\n public static prepareFromUDPPacket(headersBuffer: Buffer, checkSR = true): IVBANHeaderCommon {\n const headers: Partial<IVBANHeaderCommon> = {};\n\n // SR / Sub protocol (5 + 3 bits)\n const srsp = headersBuffer.readUInt8(PACKET_IDENTIFICATION.length);\n //take last 5 bits for sampleRate\n const srIndex = srsp & 0b00011111; // 5 last Bits\n\n if ((checkSR && !sampleRates.hasOwnProperty(srIndex)) || sampleRates[srIndex] === undefined) {\n throw new Error(`unknown sample rate ${srIndex}`);\n }\n headers.sr = sampleRates[srIndex];\n headers.srIndex = srIndex;\n\n // Samples per frame (8 bits)\n headers.part1 = headersBuffer.readUInt8(5);\n\n // Channels (8 bits)\n headers.part2 = headersBuffer.readUInt8(6);\n\n headers.part3 = headersBuffer.readUInt8(7);\n\n // Stream Name (16 bytes)\n headers.streamName = cleanPacketString(headersBuffer.toString('ascii', 8, 8 + STREAM_NAME_LENGTH));\n\n // Frame Counter (32 bits)\n headers.frameCounter = headersBuffer.readUInt32LE(24);\n\n return headers as IVBANHeaderCommon;\n }\n\n /**\n * common constructor\n */\n constructor(headers: IVBANHeader) {\n this.sr = headers.sr;\n this.streamName = headers.streamName;\n // Frame Counter (32 bits)\n this.frameCounter = headers.frameCounter ?? 1;\n }\n\n /**\n * Convert a VBANPacket to a UDP packet\n */\n protected static convertToUDPPacket(headers: Omit<IVBANHeaderCommon, 'srIndex'>, data: Buffer, sampleRate?: number): Buffer {\n let bufferStart = 0;\n\n const headersBuffer = Buffer.alloc(28);\n\n bufferStart += PACKET_IDENTIFICATION.length;\n headersBuffer.fill(PACKET_IDENTIFICATION, bufferStart - PACKET_IDENTIFICATION.length, bufferStart, 'ascii');\n\n let rate = sampleRate ?? 0;\n if (sampleRate === undefined) {\n //search sampleRate\n rate = Number(\n Object.entries(sampleRates)\n .find(([, sr]) => sr && sr === headers.sr)\n ?.shift()\n );\n if (!rate) {\n throw new Error(`fail to find index for sample rate ${headers.sr}`);\n }\n }\n\n headersBuffer.fill((rate & 0b00011111) | (headers.sp & 0b11100000), bufferStart++);\n\n headersBuffer.fill(headers.part1, bufferStart++);\n headersBuffer.fill(headers.part2, bufferStart++);\n headersBuffer.fill(headers.part3, bufferStart++);\n\n headersBuffer.fill(headers.streamName.padEnd(STREAM_NAME_LENGTH, '\\0'), bufferStart, bufferStart + STREAM_NAME_LENGTH, 'ascii');\n bufferStart += STREAM_NAME_LENGTH;\n\n headersBuffer.writeUInt32LE(headers.frameCounter ?? 1, bufferStart);\n\n if (data.length > VBAN_DATA_MAX_SIZE) {\n throw new Error(\n `VBAN DATA MAX SIZE = ${VBAN_DATA_MAX_SIZE} ! You try to send a packet with ${data.length} bytes . You can use the exported var VBAN_DATA_MAX_SIZE to split your datas in packets`\n );\n }\n\n return Buffer.concat([headersBuffer, data.subarray(0, VBAN_DATA_MAX_SIZE)]);\n }\n\n /**\n * EXPERIMENTAL - DO NOT USE\n *\n * @experimental\n */\n public static checkFrameCounter(headers: VBANPacket) {\n //check frameCounter\n const frameCounterKey = 'str';\n const frameCounter = this.frameCounters.get(frameCounterKey);\n\n if (!headers.frameCounter) {\n return;\n }\n\n if (frameCounter && frameCounter > headers.frameCounter && headers.frameCounter > 0) {\n console.log('frameCounter error');\n } else if (frameCounter && headers.frameCounter > 0) {\n console.log('frame counter', 'old', frameCounter, 'new', headers.frameCounter, 'diff', headers.frameCounter - frameCounter);\n } else if (headers.frameCounter === 0) {\n console.log('frame 0');\n }\n\n this.frameCounters.set(frameCounterKey, headers.frameCounter);\n }\n}\n", "//sub protocols\nexport enum ESubProtocol {\n AUDIO = 0x00,\n SERIAL = 0x20,\n TEXT = 0x40,\n SERVICE = 0x60\n}\n", "export const PACKET_IDENTIFICATION = 'VBAN';\n\n/**\n * the stream name is limited to 16 bytes\n */\nexport const STREAM_NAME_LENGTH = 16;\n\nexport const BITS_SPEEDS: Record<number, number> = {\n 0: 0,\n 1: 110,\n 2: 150,\n 3: 300,\n 4: 600,\n 5: 1200,\n 6: 2400,\n 7: 4800,\n 8: 9600,\n 9: 14400,\n 10: 19200,\n 11: 31250,\n 12: 38400,\n 13: 57600,\n 14: 115200,\n 15: 128000,\n 16: 230400,\n 17: 250000,\n 18: 256000,\n 19: 460800,\n 20: 921600,\n 21: 1000000,\n 22: 1500000,\n 23: 2000000,\n 24: 3000000,\n 25: 0,\n 26: 0,\n 27: 0,\n 28: 0,\n 29: 0,\n 30: 0,\n 31: 0\n};\n\nexport enum EFormatBit {\n /**\n * 0 to 255\n */\n VBAN_DATATYPE_BYTE8 = 0x00\n}\n\nexport const serialStopModes: Array<{ mode: number; stop: number | null }> = [\n {\n mode: 0,\n stop: 1\n },\n {\n mode: 1,\n stop: 1.5\n },\n {\n mode: 2,\n stop: 2\n },\n {\n mode: 3,\n stop: null\n }\n];\n\nexport function dec2bin(dec: number) {\n return ((dec >>> 0).toString(2) || '').padStart(8, '0');\n}\n\nexport function bufferToHex(buffer: Buffer) {\n if (!Buffer.isBuffer(buffer)) {\n throw new Error('need to be a buffer');\n }\n\n let hexString = '';\n for (let i = 0; i < buffer.length; i++) {\n const hex = buffer[i].toString(16).padStart(2, '0');\n hexString += hex;\n\n if (i < buffer.length - 1) {\n hexString += ' ';\n }\n }\n return hexString;\n}\n\nexport function prepareStringForPacket(str: string, maxLength: number): string {\n return str.slice(0, maxLength).padEnd(maxLength, '\\0');\n}\n\nexport function cleanPacketString(str: string): string {\n return str.replace(/\\0/g, '');\n}\n\n//sample rates\nexport const sampleRates: Record<number, number> = {\n 0: 6000,\n 1: 12000,\n 2: 24000,\n 3: 48000,\n 4: 96000,\n 5: 192000,\n 6: 384000,\n 7: 8000,\n 8: 16000,\n 9: 32000,\n 10: 64000,\n 11: 128000,\n 12: 256000,\n 13: 512000,\n 14: 11025,\n 15: 22050,\n 16: 44100,\n 17: 88200,\n 18: 176400,\n 19: 352800,\n 20: 705600,\n 21: 0,\n 22: 0,\n 23: 0,\n 24: 0,\n 25: 0,\n 26: 0,\n 27: 0,\n 28: 0,\n 29: 0,\n 30: 0,\n 31: 0\n};\n", "export const MAX_FRAME_COUNTER = 4294967295;\nexport const VBAN_DATA_MAX_SIZE = 1436;\nexport const VBAN_PACKET_MAX_SIZE = 1464;\n", "import { VBANPacket } from '../VBANPacket.js';\nimport { ESubProtocol } from '../ESubProtocol.js';\nimport { EBitsResolutions } from './EBitsResolutions.js';\nimport { ECodecs } from './ECodecs.js';\nimport { IVBANHeaderAudio } from './IVBANHeaderAudio.js';\nimport { IBitResolution } from './IBitResolution.js';\nimport { Buffer } from 'buffer';\n\nexport class VBANAudioPacket extends VBANPacket {\n /**\n * {@link VBANAudioPacket.subProtocol}\n */\n public static readonly subProtocol: ESubProtocol = ESubProtocol.AUDIO;\n public subProtocol: ESubProtocol = VBANAudioPacket.subProtocol;\n /**\n * Number of sample is given by an 8 bits unsigned integer (0 \u2013 255) where 0 means 1 sample and\n * 255 means 256 samples\n */\n public nbSample: number;\n /**\n * Number of channel is given by an 8 bits unsigned integer (0 \u2013 255) where 0 means 1 channel\n * and 255 means 256 channels.\n */\n public nbChannel: number;\n /**\n * Data type used to store audio sample in the packet\n * Use it to select the correct bitResolution {@link VBANAudioPacket.bitResolutions}, or directly use {@link VBANAudioPacket.bitResolutionObject}\n */\n public bitResolution: EBitsResolutions;\n /**\n * the bit resolution selected by the id in {@link VBANAudioPacket.bitResolution}\n */\n public readonly bitResolutionObject: IBitResolution;\n /**\n * Audio codec used\n */\n public codec: ECodecs;\n\n /**\n * current audio\n */\n public data: Buffer;\n\n constructor(headers: IVBANHeaderAudio, data: Buffer) {\n super({\n ...headers,\n sp: VBANAudioPacket.subProtocol\n });\n\n this.nbSample = headers.nbSample;\n this.nbChannel = headers.nbChannel;\n this.bitResolution = headers.bitResolution;\n if (!VBANAudioPacket.bitResolutions[headers.bitResolution]) {\n throw new Error(`fail to found bitResolution with ID ${headers.bitResolution}`);\n }\n this.bitResolutionObject = VBANAudioPacket.bitResolutions[headers.bitResolution];\n this.codec = headers.codec;\n\n this.data = data;\n }\n\n public static toUDPPacket(packet: VBANAudioPacket): Buffer {\n return this.convertToUDPPacket(\n {\n streamName: packet.streamName,\n sp: packet.subProtocol,\n sr: packet.sr,\n frameCounter: packet.frameCounter,\n part1: packet.nbSample - 1,\n part2: packet.nbChannel - 1,\n part3: (packet.bitResolution & 0b0000111) | (packet.codec & 0b11110000)\n },\n packet.data\n );\n }\n\n public static fromUDPPacket(headersBuffer: Buffer, dataBuffer: Buffer): VBANAudioPacket {\n const headers = this.prepareFromUDPPacket(headersBuffer);\n const nbSample = headers.part1 + 1;\n const nbChannel = headers.part2 + 1;\n\n // Data Format / Codec (3 + 1 + 4 bits)\n const dataFormatAndCodec = headers.part3;\n\n const bitResolution = dataFormatAndCodec & 0b0000111;\n if (!EBitsResolutions[bitResolution]) {\n throw new Error(`unknown bit resolution ${bitResolution}`);\n }\n\n const codec = dataFormatAndCodec & 0b11110000;\n if (!ECodecs[codec]) {\n throw new Error(`unknown codec ${codec}`);\n }\n\n return new VBANAudioPacket(\n {\n ...headers,\n nbSample,\n nbChannel,\n bitResolution,\n codec\n },\n dataBuffer\n );\n }\n\n public static bitResolutions: Record<number, IBitResolution> = {\n 0: { bitDepth: 8, signed: false, float: false },\n 1: { bitDepth: 16, signed: true, float: false },\n 2: { bitDepth: 24, signed: true, float: false },\n 3: { bitDepth: 32, signed: true, float: false },\n 4: { bitDepth: 32, signed: true, float: true },\n 5: { bitDepth: 64, signed: true, float: true },\n 6: { bitDepth: 12, signed: true, float: false },\n 7: { bitDepth: 10, signed: true, float: false }\n };\n}\n", "export enum ESerialStreamType {\n VBAN_SERIAL_GENERIC = 0x00,\n VBAN_SERIAL_MIDI = 0x10,\n VBAN_SERIAL_UNDEFINED_2 = 0x20,\n VBAN_SERIAL_UNDEFINED_3 = 0x30,\n VBAN_SERIAL_UNDEFINED_4 = 0x40,\n VBAN_SERIAL_UNDEFINED_5 = 0x50,\n VBAN_SERIAL_UNDEFINED_6 = 0x60,\n VBAN_SERIAL_UNDEFINED_7 = 0x70,\n VBAN_SERIAL_UNDEFINED_8 = 0x80,\n VBAN_SERIAL_UNDEFINED_9 = 0x90,\n VBAN_SERIAL_UNDEFINED_10 = 0xa0,\n VBAN_SERIAL_UNDEFINED_11 = 0xb0,\n VBAN_SERIAL_UNDEFINED_12 = 0xc0,\n VBAN_SERIAL_UNDEFINED_13 = 0xd0,\n VBAN_SERIAL_UNDEFINED_14 = 0xe0,\n VBAN_SERIAL_USER = 0xf0\n}\n", "import { VBANPacket } from '../VBANPacket.js';\nimport { ESubProtocol } from '../ESubProtocol.js';\nimport { BITS_SPEEDS, EFormatBit, serialStopModes } from '../../commons.js';\nimport { ISerialBitMode } from './ISerialBitMode.js';\nimport { ESerialStreamType } from './ESerialStreamType.js';\nimport { IVBANHeaderSerial } from './IVBANHeaderSerial.js';\nimport { Buffer } from 'buffer';\n\nexport class VBANSerialPacket extends VBANPacket {\n /**\n * {@link VBANSerialPacket.subProtocol}\n */\n public static readonly subProtocol: ESubProtocol = ESubProtocol.SERIAL;\n public subProtocol: ESubProtocol = VBANSerialPacket.subProtocol;\n /**\n * This field is used to give possible information on COM port and serial transmission mode related\n * to a Hardware COM port. This is made to possibly emulate COM to COM port connections and\n * let the receiver configure the physical COM port in the right mode.\n */\n public bitMode: ISerialBitMode;\n /**\n * Can be used to define a sub channel (sub serial link) and then manage up to 256 different\n * serial virtual pipes (ZERO by default).\n */\n public channelsIdents: number;\n /**\n * SR / bps : Bit rate is given in bps for information only. But it can be useful if serial data come from or go to\n * a particular COM port. Set to ZERO if there is no particular bit rate.\n */\n public bps: number;\n /**\n * not used . Replaced by {@link VBANSerialPacket.bps}\n */\n public sr: number = 0;\n /**\n * Data type used to store data in the packet (ZERO per default). The index is stored on 3 first bits.\n * Bit 3 must be ZERO. Bits 4 to 7 gives additional mode\n */\n public formatBit: EFormatBit;\n /**\n * type of stream . MIDI or SERIAL ... But in practice, only serial is used (MIDI is serial)\n */\n public streamType: ESerialStreamType;\n\n public data: Buffer;\n\n constructor(headers: IVBANHeaderSerial, data: Buffer) {\n super({\n ...headers,\n sp: VBANSerialPacket.subProtocol,\n sr: 0\n });\n\n this.bitMode = headers.bitMode;\n this.channelsIdents = headers.channelsIdents;\n this.bps = headers.bps;\n this.formatBit = headers.formatBit;\n this.streamType = headers.streamType;\n\n this.data = data;\n\n //reset sr\n this.sr = 0;\n }\n\n public static toUDPPacket(packet: VBANSerialPacket): Buffer {\n let part1 = 0;\n\n const mode = serialStopModes.find((m) => m.stop === packet.bitMode.stop)?.mode;\n if (mode === undefined) {\n throw new Error(`fail to found mode for stop ${packet.bitMode.stop}`);\n }\n part1 |= mode & 0b00000011;\n\n if (packet.bitMode.start) {\n part1 |= 0b00000100;\n }\n\n if (packet.bitMode.parity) {\n part1 |= 0b00001000;\n }\n\n if (packet.bitMode.multipart) {\n part1 |= 0b10000000;\n }\n\n //search bpsId\n const bpsId =\n Number(\n Object.entries(BITS_SPEEDS)\n .find(([, bps]) => bps && bps === packet.bps)\n ?.shift()\n ) || 0;\n\n return this.convertToUDPPacket(\n {\n streamName: packet.streamName,\n sp: packet.subProtocol,\n sr: packet.bps,\n frameCounter: packet.frameCounter,\n part1,\n part2: packet.channelsIdents,\n part3: (packet.formatBit & 0b00000111) | (packet.streamType & 0b11110000)\n },\n packet.data,\n bpsId\n );\n }\n\n public static fromUDPPacket(headersBuffer: Buffer, dataBuffer: Buffer): VBANSerialPacket {\n const headers = this.prepareFromUDPPacket(headersBuffer, false);\n\n if (headers.srIndex === undefined || BITS_SPEEDS[headers.srIndex] === undefined) {\n throw new Error(`unknown bits speed ${headers.srIndex}`);\n }\n\n const bps = BITS_SPEEDS[headers.srIndex];\n\n const bitModeRaw = headers.part1;\n\n const stopMode = bitModeRaw & 0b00000011;\n\n const stop = serialStopModes.find((m) => m.mode === stopMode)?.stop ?? null;\n\n const start = (bitModeRaw & 0b00000100) === 4;\n const parity = (bitModeRaw & 0b00001000) === 8;\n const multipart = (bitModeRaw & 0b10000000) === 128;\n\n const bitMode = {\n stop,\n start,\n parity,\n multipart\n };\n\n const channelsIdents = headers.part2;\n\n const dataFormat = headers.part3;\n const formatBit = dataFormat & 0b00000111;\n if (!EFormatBit[formatBit]) {\n throw new Error(`unknown format bit ${formatBit}`);\n }\n\n const streamType = dataFormat & 0b11110000;\n if (!ESerialStreamType[streamType]) {\n throw new Error(`unknown stream type ${streamType}`);\n }\n\n return new VBANSerialPacket(\n {\n ...headers,\n bps,\n bitMode,\n channelsIdents,\n formatBit,\n streamType\n },\n dataBuffer\n );\n }\n}\n", "export enum EServiceFunction {\n PING0 = 0x00,\n REPLY = 0x80\n}\n", "export enum EServicePINGApplicationType {\n UNKNOWN = 0x00000000,\n RECEPTOR = 0x00000001,\n TRANSMITTER = 0x00000002,\n RECEPTORSPOT = 0x00000004,\n TRANSMITTERSPOT = 0x00000008,\n VIRTUALDEVICE = 0x00000010,\n VIRTUALMIXER = 0x00000020,\n MATRIX = 0x00000040,\n DAW = 0x00000080,\n SERVER = 0x01000000\n}\n", "export enum EServicePINGFeatures {\n AUDIO = 0x00000001,\n AOIP = 0x00000002,\n VOIP = 0x00000004,\n SERIAL = 0x00000100,\n MIDI = 0x00000300,\n FRAME = 0x00001000,\n TXT = 0x00010000\n}\n", "export enum EServiceType {\n IDENTIFICATION = 0,\n CHATUTF8 = 1,\n RTPACKETREGISTER = 32,\n RTPACKET = 33\n}\n", "import { Buffer } from 'buffer';\nimport { VBANPacket } from '../VBANPacket.js';\nimport { ESubProtocol } from '../ESubProtocol.js';\nimport { EServiceType } from './EServiceType.js';\nimport { IVBANHeaderService } from './IVBANHeaderService.js';\nimport { EServiceFunction } from './EServiceFunction.js';\n\nexport class VBANServicePacket extends VBANPacket {\n /**\n * {@link VBANServicePacket.subProtocol}\n */\n public static readonly subProtocol: ESubProtocol = ESubProtocol.SERVICE;\n public subProtocol: ESubProtocol = VBANServicePacket.subProtocol;\n /**\n * Sub Type of the service packet\n * {@link EServiceType}\n */\n public service: EServiceType;\n /**\n * current function for this function\n */\n public serviceFunction: EServiceFunction;\n /**\n * answer is a reply to another request\n */\n public isReply: boolean = false;\n\n public data: unknown;\n\n /**\n * not used .\n */\n public sr: number = 0;\n\n constructor(headers: IVBANHeaderService) {\n super({\n ...headers,\n sp: VBANServicePacket.subProtocol,\n sr: 0\n });\n\n this.service = headers.service;\n this.serviceFunction = headers.serviceFunction;\n this.isReply = headers.isReply ?? false;\n\n //force sr to 0\n this.sr = 0;\n }\n\n public toUDPPacket(): ReturnType<(typeof VBANServicePacket)['toUDPPacket']> {\n return VBANServicePacket.toUDPPacket(this);\n }\n\n public static toUDPPacket(packet: VBANServicePacket): Buffer {\n return VBANServicePacket.convertToUDPPacket(\n {\n streamName: packet.streamName,\n sp: packet.subProtocol,\n sr: packet.sr,\n frameCounter: packet.frameCounter,\n part1: ((packet.isReply ? 0b10000000 : 0) & 0b10000000) | (packet.serviceFunction & 0b01111111),\n part2: packet.service,\n part3: 0\n },\n Buffer.from(''),\n packet.sr\n );\n }\n}\n", "import { VBANServicePacket } from './VBANServicePacket.js';\nimport { IVBANHeaderService } from './IVBANHeaderService.js';\nimport { EServiceType } from './EServiceType.js';\nimport { prepareStringForPacket } from '../../commons.js';\nimport { IVBANHeaderCommon } from '../IVBANHeaderCommon.js';\nimport { Buffer } from 'buffer';\n\nexport class VBANChatPacket extends VBANServicePacket {\n public data: string;\n constructor(headers: IVBANHeaderService, data: string) {\n super(headers);\n\n this.data = data;\n }\n\n public static fromUDPPacket(headers: IVBANHeaderCommon, dataBuffer: Buffer): VBANChatPacket {\n const fn = headers.part1;\n const serviceFunction = fn & 0b01111111;\n const isReply = (fn & 0b10000000) >= 1;\n\n return new VBANChatPacket(\n {\n ...headers,\n service: EServiceType.CHATUTF8,\n serviceFunction,\n isReply\n },\n dataBuffer.toString()\n );\n }\n\n public toUDPPacket(): ReturnType<(typeof VBANChatPacket)['toUDPPacket']> {\n return VBANChatPacket.toUDPPacket(this);\n }\n\n public static toUDPPacket(packet: VBANChatPacket): Buffer {\n // 704 is the size for a service packet\n const dataBuffer = Buffer.alloc(676);\n dataBuffer.write(prepareStringForPacket(packet.data, 676), 0, 'utf8');\n\n return this.convertToUDPPacket(\n {\n streamName: packet.streamName,\n sp: packet.subProtocol,\n sr: packet.sr,\n frameCounter: packet.frameCounter,\n part1: ((packet.isReply ? 0b10000000 : 0) & 0b10000000) | (packet.serviceFunction & 0b01111111),\n part2: packet.service,\n part3: 0\n },\n dataBuffer,\n packet.sr\n );\n }\n}\n", "import { VBANServicePacket } from './VBANServicePacket.js';\nimport { IVBANHeaderService } from './IVBANHeaderService.js';\nimport { IPacketPingData } from './IPacketPingData.js';\nimport { Buffer } from 'buffer';\nimport { EServiceType } from './EServiceType.js';\nimport { EServicePINGApplicationType } from './EServicePINGApplicationType.js';\nimport { EServicePINGFeatures } from './EServicePINGFeatures.js';\nimport { cleanPacketString, prepareStringForPacket } from '../../commons.js';\nimport { IVBANHeaderCommon } from '../IVBANHeaderCommon.js';\n\nexport class VBANPingPacket extends VBANServicePacket {\n public data: IPacketPingData;\n constructor(headers: IVBANHeaderService, data: IPacketPingData) {\n super(headers);\n\n this.data = data;\n }\n\n public static fromUDPPacket(headers: IVBANHeaderCommon, dataBuffer: Buffer): VBANServicePacket {\n const fn = headers.part1;\n const serviceFunction = fn & 0b01111111;\n const isReply = (fn & 0b10000000) >= 1;\n\n let currentByte = 0;\n const getXNextBytes = (size: number): Buffer => {\n const b = dataBuffer.subarray(currentByte, currentByte + size);\n currentByte += size;\n return b;\n };\n\n const bitType = getXNextBytes(4).readUInt32LE();\n const applicationType = EServicePINGApplicationType[bitType] ? bitType : EServicePINGApplicationType.UNKNOWN;\n const bitFeature = getXNextBytes(4).readUInt32LE();\n const features = (\n Object.entries(EServicePINGFeatures).filter(([k]) => Number.isNaN(Number(k))) as Array<[string, EServicePINGFeatures]>\n )\n .filter(([, v]) => bitFeature & v)\n .map(([, v]) => v);\n const bitFeatureEx = getXNextBytes(4).readUInt32LE();\n const PreferredRate = getXNextBytes(4).readUInt32LE();\n const minRate = getXNextBytes(4).readUInt32LE();\n const maxRate = getXNextBytes(4).readUInt32LE();\n const colorRGB = getXNextBytes(4).readUInt32LE();\n const color = {\n blue: colorRGB & 255,\n green: (colorRGB >> 8) & 255,\n red: (colorRGB >> 16) & 255\n };\n const nVersion = getXNextBytes(4).readUInt32LE();\n const GPSPosition = cleanPacketString(getXNextBytes(8).toString('ascii'));\n const userPosition = cleanPacketString(getXNextBytes(8).toString('ascii'));\n const langCode = cleanPacketString(getXNextBytes(8).toString('ascii'));\n const reservedASCII = cleanPacketString(getXNextBytes(8).toString('ascii'));\n const reservedEx = cleanPacketString(getXNextBytes(64).toString('ascii'));\n const reservedEx2 = cleanPacketString(getXNextBytes(36).toString('ascii'));\n const deviceName = cleanPacketString(getXNextBytes(64).toString('ascii'));\n const manufacturerName = cleanPacketString(getXNextBytes(64).toString('ascii'));\n const applicationName = cleanPacketString(getXNextBytes(64).toString('ascii'));\n const hostnameASCII = cleanPacketString(getXNextBytes(64).toString('ascii'));\n const userName = cleanPacketString(getXNextBytes(128).toString('utf8'));\n const userComment = cleanPacketString(getXNextBytes(128).toString('utf8'));\n\n //extract information\n const data: IPacketPingData = {\n applicationType,\n features,\n bitFeatureEx,\n PreferredRate,\n minRate,\n maxRate,\n color,\n nVersion,\n GPSPosition,\n userPosition,\n langCode,\n reservedASCII,\n reservedEx,\n reservedEx2,\n deviceName,\n manufacturerName,\n applicationName,\n hostname: hostnameASCII,\n userName,\n userComment\n };\n\n return new VBANPingPacket(\n {\n ...headers,\n service: EServiceType.IDENTIFICATION,\n serviceFunction,\n isReply\n },\n data\n );\n }\n\n public toUDPPacket(): ReturnType<(typeof VBANPingPacket)['toUDPPacket']> {\n return VBANPingPacket.toUDPPacket(this);\n }\n\n public static toUDPPacket(packet: VBANPingPacket): Buffer {\n // 704 is the size for a service packet\n const dataBuffer = Buffer.alloc(676);\n let offset = 0;\n\n offset = dataBuffer.writeUInt32LE(packet.data.applicationType, offset);\n let features = 0;\n packet.data.features.forEach((feature) => {\n if (EServicePINGFeatures[feature]) {\n features = features | feature;\n }\n });\n offset = dataBuffer.writeUInt32LE(features, offset);\n offset = dataBuffer.writeUInt32LE(packet.data.bitFeatureEx, offset);\n offset = dataBuffer.writeUInt32LE(packet.data.PreferredRate, offset);\n offset = dataBuffer.writeUInt32LE(packet.data.minRate, offset);\n offset = dataBuffer.writeUInt32LE(packet.data.maxRate, offset);\n\n const { red, green, blue } = packet.data.color;\n offset = dataBuffer.writeUInt32LE(((red & 255) << 16) | ((green & 255) << 8) | (blue & 255), offset);\n\n offset = dataBuffer.writeUInt32LE(packet.data.nVersion, offset);\n\n offset += dataBuffer.write(prepareStringForPacket(packet.data.GPSPosition, 8), offset, 'ascii');\n offset += dataBuffer.write(prepareStringForPacket(packet.data.userPosition, 8), offset, 'ascii');\n offset += dataBuffer.write(prepareStringForPacket(packet.data.langCode, 8), offset, 'ascii');\n offset += dataBuffer.write(prepareStringForPacket(packet.data.reservedASCII, 8), offset, 'ascii');\n offset += dataBuffer.write(prepareStringForPacket(packet.data.reservedEx, 64), offset, 'ascii');\n offset += dataBuffer.write(prepareStringForPacket(packet.data.reservedEx2, 36), offset, 'ascii');\n offset += dataBuffer.write(prepareStringForPacket(packet.data.deviceName, 64), offset, 'ascii');\n offset += dataBuffer.write(prepareStringForPacket(packet.data.manufacturerName, 64), offset, 'ascii');\n offset += dataBuffer.write(prepareStringForPacket(packet.data.applicationName, 64), offset, 'ascii');\n offset += dataBuffer.write(prepareStringForPacket(packet.data.hostname, 64), offset, 'ascii');\n offset += dataBuffer.write(prepareStringForPacket(packet.data.userName, 128), offset, 'utf8');\n dataBuffer.write(prepareStringForPacket(packet.data.userComment, 128), offset, 'utf8');\n\n return this.convertToUDPPacket(\n {\n streamName: packet.streamName,\n sp: packet.subProtocol,\n sr: packet.sr,\n frameCounter: packet.frameCounter,\n part1: ((packet.isReply ? 0b10000000 : 0) & 0b10000000) | (packet.serviceFunction & 0b01111111),\n part2: packet.service,\n part3: 0\n },\n dataBuffer,\n packet.sr\n );\n }\n}\n", "import { VBANServicePacket } from './VBANServicePacket.js';\nimport { IVBANHeaderService } from './IVBANHeaderService.js';\nimport { EServiceType } from './EServiceType.js';\nimport { IVBANHeaderCommon } from '../IVBANHeaderCommon.js';\nimport { Buffer } from 'buffer';\n\nexport class VBANRealTimePacket extends VBANServicePacket {\n /**\n * not clear about the content of this buffer\n */\n public data: Buffer;\n constructor(headers: IVBANHeaderService, data: Buffer) {\n super(headers);\n\n this.data = data;\n }\n\n public static fromUDPPacket(headers: IVBANHeaderCommon, dataBuffer: Buffer): VBANRealTimePacket {\n const fn = headers.part1;\n const serviceFunction = fn & 0b01111111;\n const isReply = (fn & 0b10000000) >= 1;\n\n return new VBANRealTimePacket(\n {\n ...headers,\n service: EServiceType.RTPACKET,\n serviceFunction,\n isReply\n },\n dataBuffer\n );\n }\n\n public toUDPPacket(): ReturnType<(typeof VBANRealTimePacket)['toUDPPacket']> {\n return VBANRealTimePacket.toUDPPacket(this);\n }\n\n public static toUDPPacket(packet: VBANRealTimePacket): Buffer {\n return this.convertToUDPPacket(\n {\n streamName: packet.streamName,\n sp: packet.subProtocol,\n sr: packet.sr,\n frameCounter: packet.frameCounter,\n part1: ((packet.isReply ? 0b10000000 : 0) & 0b10000000) | (packet.serviceFunction & 0b01111111),\n part2: packet.service,\n part3: 0\n },\n Buffer.from(''),\n packet.sr\n );\n }\n}\n", "import { Buffer } from 'buffer';\nimport { VBANServicePacket } from './VBANServicePacket.js';\nimport { IVBANHeaderService } from './IVBANHeaderService.js';\nimport { EServiceType } from './EServiceType.js';\nimport { IVBANHeaderCommon } from '../IVBANHeaderCommon.js';\n\nexport enum ERegistrationAnswer {\n /**\n * no RT packet service (could mean the packet ID is not existing).\n */\n NO_RT_PACKET_SERVICE = 0,\n /**\n * RT packet service registered\n */\n RT_PACKET_SERVICE_REGISTERED = 1,\n /**\n * RT packet service busy (no more slot).\n */\n RT_PACKET_SERVICE_BUSY = 2\n}\n\nexport interface IRealTimeRegisterAnswerPacket {\n /**\n * Registration answer\n */\n answer: ERegistrationAnswer;\n}\n\nexport class VBANRealTimeRegisterAnswerPacket extends VBANServicePacket {\n public data: IRealTimeRegisterAnswerPacket;\n constructor(headers: IVBANHeaderService, data: IRealTimeRegisterAnswerPacket) {\n super(headers);\n\n this.data = data;\n }\n\n public static fromUDPPacket(headers: IVBANHeaderCommon): VBANRealTimeRegisterAnswerPacket {\n const fn = headers.part1;\n const serviceFunction = fn & 0b01111111;\n const isReply = (fn & 0b10000000) >= 1;\n\n return new VBANRealTimeRegisterAnswerPacket(\n {\n ...headers,\n service: EServiceType.RTPACKETREGISTER,\n serviceFunction,\n isReply\n },\n {\n answer: headers.part3\n }\n );\n }\n\n public toUDPPacket(): ReturnType<(typeof VBANRealTimeRegisterAnswerPacket)['toUDPPacket']> {\n return VBANRealTimeRegisterAnswerPacket.toUDPPacket(this);\n }\n\n public static toUDPPacket(packet: VBANRealTimeRegisterAnswerPacket): Buffer {\n return this.convertToUDPPacket(\n {\n streamName: packet.streamName,\n sp: packet.subProtocol,\n sr: packet.sr,\n frameCounter: packet.frameCounter,\n part1: ((packet.isReply ? 0b10000000 : 0) & 0b10000000) | (packet.serviceFunction & 0b01111111),\n part2: packet.service,\n part3: packet.data.answer\n },\n Buffer.from(''),\n packet.sr\n );\n }\n}\n", "import { Buffer } from 'buffer';\nimport { VBANServicePacket } from './VBANServicePacket.js';\nimport { IVBANHeaderService } from './IVBANHeaderService.js';\nimport { EServiceType } from './EServiceType.js';\nimport { IVBANHeaderCommon } from '../IVBANHeaderCommon.js';\nimport { VBANRealTimeRegisterAnswerPacket } from './VBANRealTimeRegisterAnswerPacket.js';\n\nexport interface IRealTimeRegisterPacket {\n /**\n * Time out in second (to stop RT packet broadcast)\n */\n timeout: number;\n}\n\nexport class VBANRealTimeRegisterPacket extends VBANServicePacket {\n public data: IRealTimeRegisterPacket;\n constructor(headers: IVBANHeaderService, data: IRealTimeRegisterPacket) {\n super(headers);\n\n this.data = data;\n }\n\n public static fromUDPPacket(headers: IVBANHeaderCommon): VBANRealTimeRegisterPacket | VBANRealTimeRegisterAnswerPacket {\n const fn = headers.part1;\n const serviceFunction = fn & 0b01111111;\n const isReply = (fn & 0b10000000) >= 1;\n\n if (isReply) {\n return VBANRealTimeRegisterAnswerPacket.fromUDPPacket(headers);\n }\n\n return new VBANRealTimeRegisterPacket(\n {\n ...headers,\n service: EServiceType.RTPACKETREGISTER,\n serviceFunction,\n isReply\n },\n {\n timeout: headers.part3\n }\n );\n }\n\n public toUDPPacket(): ReturnType<(typeof VBANRealTimeRegisterPacket)['toUDPPacket']> {\n return VBANRealTimeRegisterPacket.toUDPPacket(this);\n }\n\n public static toUDPPacket(packet: VBANRealTimeRegisterPacket): Buffer {\n if (packet.data.timeout > 255 || packet.data.timeout < 0) {\n throw new Error('timeout need to be between 0 and 255');\n }\n\n return this.convertToUDPPacket(\n {\n streamName: packet.streamName,\n sp: packet.subProtocol,\n sr: packet.sr,\n frameCounter: packet.frameCounter,\n part1: ((packet.isReply ? 0b10000000 : 0) & 0b10000000) | (packet.serviceFunction & 0b01111111),\n part2: packet.service,\n part3: packet.data.timeout\n },\n Buffer.from(''),\n packet.sr\n );\n }\n}\n", "import { Buffer } from 'buffer';\nimport { VBANServicePacket } from './VBANServicePacket.js';\nimport { EServiceType } from './EServiceType.js';\nimport { VBANPacket } from '../VBANPacket.js';\nimport { VBANPingPacket } from './VBANPingPacket.js';\nimport { VBANChatPacket } from './VBANChatPacket.js';\nimport { VBANRealTimeRegisterPacket } from './VBANRealTimeRegisterPacket.js';\nimport { VBANRealTimePacket } from './VBANRealTimePacket.js';\n\nexport class VBANServicePacketFactory {\n public static fromUDPPacket(headersBuffer: Buffer, dataBuffer: Buffer): VBANServicePacket {\n const headers = VBANPacket.prepareFromUDPPacket(headersBuffer);\n const service = headers.part2;\n\n return this.getConstructor(service).fromUDPPacket(headers, dataBuffer);\n }\n\n private static getConstructor(\n protocol: EServiceType\n ): typeof VBANPingPacket | typeof VBANChatPacket | typeof VBANRealTimeRegisterPacket | typeof VBANRealTimePacket {\n switch (protocol) {\n case EServiceType.IDENTIFICATION:\n return VBANPingPacket;\n case EServiceType.CHATUTF8:\n return VBANChatPacket;\n case EServiceType.RTPACKET:\n return VBANRealTimePacket;\n case EServiceType.RTPACKETREGISTER:\n return VBANRealTimeRegisterPacket;\n default:\n throw new Error(`unknown protocol ${protocol}`);\n }\n }\n\n static toUDPPacket(packet: VBANServicePacket): Buffer {\n return packet.toUDPPacket();\n }\n}\n", "export enum ETextEncoding {\n VBAN_TXT_ASCII = 0x00,\n VBAN_TXT_UTF8 = 0x10,\n VBAN_TXT_WCHAR = 0x20,\n VBAN_SERIAL_UNDEFINED_3 = 0x30,\n VBAN_SERIAL_UNDEFINED_4 = 0x40,\n VBAN_SERIAL_UNDEFINED_5 = 0x50,\n VBAN_SERIAL_UNDEFINED_6 = 0x60,\n VBAN_SERIAL_UNDEFINED_7 = 0x70,\n VBAN_SERIAL_UNDEFINED_8 = 0x80,\n VBAN_SERIAL_UNDEFINED_9 = 0x90,\n VBAN_SERIAL_UNDEFINED_10 = 0xa0,\n VBAN_SERIAL_UNDEFINED_11 = 0xb0,\n VBAN_SERIAL_UNDEFINED_12 = 0xc0,\n VBAN_SERIAL_UNDEFINED_13 = 0xd0,\n VBAN_SERIAL_UNDEFINED_14 = 0xe0,\n VBAN_SERIAL_USER = 0xf0\n}\n", "import { VBANPacket } from '../VBANPacket.js';\nimport { Buffer } from 'buffer';\nimport { ESubProtocol } from '../ESubProtocol.js';\nimport { BITS_SPEEDS, EFormatBit } from '../../commons.js';\nimport { ETextEncoding } from './ETextEncoding.js';\nimport { IVBANHeaderTEXT } from './IVBANHeaderTEXT.js';\n\nexport class VBANTEXTPacket extends VBANPacket {\n /**\n * {@link VBANTEXTPacket.subProtocol}\n */\n public static readonly subProtocol: ESubProtocol = ESubProtocol.TEXT;\n public subProtocol: ESubProtocol = VBANTEXTPacket.subProtocol;\n /**\n * Bit rate is given in bps for information only. But it can be used internally to limit the bandwidth of\n * the stream and for example gives more priority to audio stream or RT MIDI stream. It can be set\n * to ZERO if there is no particular bit rate.\n */\n public bps: number;\n /**\n * Can be used to define a sub channel (sub text channel) and then manage up to 256 different\n * virtual pipes (ZERO by default).\n */\n public channelsIdents: number;\n /**\n * Data type used to store data in the packet (ZERO/VBAN_DATATYPE_BYTE8 per default).\n */\n public formatBit: EFormatBit;\n /**\n * Text format\n */\n public encoding: ETextEncoding;\n /**\n * not used . Replaced by {@link VBANTEXTPacket.bps}\n */\n sr: number;\n /**\n * if data can be decoded, it will be decoded in text\n */\n public text: string;\n /**\n * you can access the raw dataBuffer (if available) to try another decoding\n */\n public dataBuffer?: Buffer;\n\n constructor(headers: IVBANHeaderTEXT, txt: string = '', dataBuffer?: Buffer) {\n super({\n ...headers,\n sp: VBANTEXTPacket.subProtocol,\n sr: 0\n });\n\n this.bps = headers.bps ?? BITS_SPEEDS[0];\n this.channelsIdents = headers.channelsIdents ?? 0;\n this.formatBit = headers.formatBit ?? EFormatBit.VBAN_DATATYPE_BYTE8;\n this.encoding = headers.encoding;\n\n this.text = txt;\n this.dataBuffer = dataBuffer;\n\n //force sr to 0\n this.sr = 0;\n }\n\n public static toUDPPacket(packet: VBANTEXTPacket): Buffer {\n const data = packet.text\n ? Buffer.from(packet.text, VBANTEXTPacket.getEncoding(packet.encoding))\n : (packet.dataBuffer ?? Buffer.from(''));\n\n const bpsId =\n Number(\n Object.entries(BITS_SPEEDS)\n .find(([, bps]) => bps && bps === packet.bps)\n ?.shift()\n ) || 0;\n\n return this.convertToUDPPacket(\n {\n streamName: packet.streamName,\n sp: packet.subProtocol,\n sr: packet.bps,\n frameCounter: packet.frameCounter,\n part1: 0,\n part2: packet.channelsIdents,\n part3: (packet.formatBit & 0b00000111) | (packet.encoding & 0b11110000)\n },\n data,\n bpsId\n );\n }\n\n public static fromUDPPacket(headersBuffer: Buffer, dataBuffer: Buffer) {\n const headers = this.prepareFromUDPPacket(headersBuffer);\n\n if (headers.srIndex === undefined || BITS_SPEEDS[headers.srIndex] === undefined) {\n throw new Error(`unknown bits speed ${headers.srIndex}`);\n }\n\n const bps = BITS_SPEEDS[headers.srIndex];\n\n const channelsIdents = headers.part2;\n\n const dataFormat = headers.part3;\n const formatBit = dataFormat & 0b00000111;\n if (!EFormatBit[formatBit]) {\n throw new Error(`unknown format bit ${formatBit}`);\n }\n\n const encoding = dataFormat & 0b11110000;\n if (!ETextEncoding[encoding]) {\n throw new Error(`unknown text stream type ${encoding}`);\n }\n\n const textEncoding = VBANTEXTPacket.getEncoding(encoding);\n\n let text;\n if (textEncoding) {\n text = dataBuffer.toString(textEncoding);\n }\n\n return new VBANTEXTPacket(\n {\n ...headers,\n bps,\n channelsIdents,\n formatBit,\n encoding\n },\n text,\n dataBuffer\n );\n }\n\n static getEncoding(streamType: ETextEncoding): BufferEncoding | undefined {\n let textEncoding: BufferEncoding | undefined;\n if (streamType === ETextEncoding.VBAN_TXT_UTF8) {\n textEncoding = 'utf8';\n } else if (streamType === ETextEncoding.VBAN_TXT_WCHAR) {\n //need to test this, voicemeeter seems to don't use it\n textEncoding = 'utf16le';\n } else if (streamType === ETextEncoding.VBAN_TXT_ASCII) {\n textEncoding = 'ascii';\n }\n\n return textEncoding;\n }\n}\n", "import { Buffer } from 'buffer';\nimport { PACKET_IDENTIFICATION } from './commons.js';\nimport {\n ESubProtocol,\n VBANAudioPacket,\n VBANPacket,\n VBANSerialPacket,\n VBANServicePacket,\n VBANServicePacketFactory,\n VBANTEXTPacket\n} from './packets/index.js';\n\nexport class VBANProtocolFactory {\n public static processPacket(packet: Buffer): VBANAudioPacket | VBANSerialPacket | VBANTEXTPacket | VBANServicePacket {\n const headerBuffer = packet.subarray(0, 28);\n const dataBuffer = packet.subarray(28);\n\n if (headerBuffer.toString('ascii', 0, PACKET_IDENTIFICATION.length) !== PACKET_IDENTIFICATION) {\n throw new Error('Invalid Header');\n }\n\n // SR / Sub protocol (5 + 3 bits)\n const header1 = headerBuffer.readUInt8(PACKET_IDENTIFICATION.length);\n\n // first 3 bits only\n const subProtocol: ESubProtocol = header1 & 0b11100000;\n\n return VBANProtocolFactory.getConstructor(subProtocol).fromUDPPacket(headerBuffer, dataBuffer);\n }\n\n public static getConstructor(\n protocol: ESubProtocol\n ): typeof VBANAudioPacket | typeof VBANSerialPacket | typeof VBANTEXTPacket | typeof VBANServicePacketFactory {\n switch (protocol) {\n case ESubProtocol.AUDIO:\n return VBANAudioPacket;\n case ESubProtocol.SERIAL:\n return VBANSerialPacket;\n case ESubProtocol.TEXT:\n return VBANTEXTPacket;\n case ESubProtocol.SERVICE:\n return VBANServicePacketFactory;\n default:\n throw new Error(`unknown protocol ${protocol}`);\n }\n }\n\n public static toUDPBuffer(packet: Pick<VBANPacket, 'subProtocol'>): Buffer {\n switch (packet.subProtocol) {\n case ESubProtocol.AUDIO:\n return VBANAudioPacket.toUDPPacket(packet as VBANAudioPacket);\n case ESubProtocol.SERIAL:\n return VBANSerialPacket.toUDPPacket(packet as VBANSerialPacket);\n case ESubProtocol.TEXT:\n return VBANTEXTPacket.toUDPPacket(packet as VBANTEXTPacket);\n case ESubProtocol.SERVICE:\n return VBANServicePacketFactory.toUDPPacket(packet as VBANServicePacket);\n default:\n throw new Error('unknown packet instance');\n }\n }\n}\n", "import dgram, { BindOptions, RemoteInfo, Socket } from 'node:dgram';\nimport type { AddressInfo } from 'net';\nimport { EventEmitter } from 'events';\nimport {\n EServiceFunction,\n EServicePINGApplicationType,\n EServicePINGFeatures,\n EServiceType,\n ESubProtocol,\n IPacketPingData,\n MAX_FRAME_COUNTER,\n VBANPacket,\n VBANPacketTypes,\n VBANPingPacket\n} from './packets/index.js';\nimport { VBANProtocolFactory } from './VBANProtocolFactory.js';\nimport { IVBANServerOptions } from './IVBANServerOptions.js';\nimport { promisify } from 'node:util';\nimport os from 'node:os';\n\nexport interface VBANServerEvents {\n listening: () => void;\n error: (err: Error) => void;\n close: () => void;\n message: (packet: VBANPacketTypes, sender: RemoteInfo) => void;\n}\n\nexport declare interface VBANServer {\n on<U extends keyof VBANServerEvents>(event: U, listener: VBANServerEvents[U]): this;\n\n emit<U extends keyof VBANServerEvents>(event: U, ...args: Parameters<VBANServerEvents[U]>): boolean;\n}\n\nexport class VBANServer extends EventEmitter {\n public readonly UDPServer: Socket;\n private readonly options: IVBANServerOptions;\n\n private readonly frameCounter: Map<ESubProtocol, number> = new Map<ESubProtocol, number>();\n\n public isListening = false;\n\n constructor(options?: IVBANServerOptions) {\n super();\n this.UDPServer = dgram.createSocket('udp4');\n\n this.options = options || {};\n if (this.options.autoReplyToPing === undefined) {\n this.options.autoReplyToPing = true;\n }\n\n