UNPKG

@neurosity/sdk

Version:
117 lines (116 loc) 4.83 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.encodedSampleSize = exports.decode = exports.binaryBufferToSamples = exports.binaryBufferToEpoch = void 0; const rxjs_1 = require("rxjs"); const operators_1 = require("rxjs/operators"); const index_js_1 = require("buffer/index.js"); // not including /index.js causes typescript to uses Node's native Buffer built-in and we want to use this npm package for both node and the browser const pipes_1 = require("../../../utils/pipes"); const EPOCH_BUFFER_SIZE = 16; const SAMPLING_RATE_FALLBACK = 256; // Crown's sampling rate /** Size in bytes for each channel's payload. */ const TimestampSize = 8; // UInt64 const MarkerSize = 2; // UInt16 const ChannelDataSize = 8; // Double /** Size in bytes for the static payload of every sample (Timestamp + Marker) */ const SampleFixedSize = TimestampSize + MarkerSize; /** * @hidden */ function binaryBufferToEpoch(deviceInfo) { var _a; if (!(deviceInfo === null || deviceInfo === void 0 ? void 0 : deviceInfo.samplingRate)) { console.warn(`Didn't receive a sampling rate, defaulting to ${SAMPLING_RATE_FALLBACK}`); } return (0, rxjs_1.pipe)(binaryBufferToSamples(deviceInfo.channels), (0, pipes_1.epoch)({ duration: EPOCH_BUFFER_SIZE, interval: EPOCH_BUFFER_SIZE, samplingRate: (_a = deviceInfo === null || deviceInfo === void 0 ? void 0 : deviceInfo.samplingRate) !== null && _a !== void 0 ? _a : SAMPLING_RATE_FALLBACK }), (0, pipes_1.addInfo)({ channelNames: deviceInfo.channelNames, samplingRate: deviceInfo.samplingRate })); } exports.binaryBufferToEpoch = binaryBufferToEpoch; /** * @hidden */ function binaryBufferToSamples(channelCount) { return (0, rxjs_1.pipe)((0, operators_1.mergeMap)((arrayBuffer) => { const buffer = index_js_1.Buffer.from(arrayBuffer); const decoded = decode(buffer, channelCount); return (0, rxjs_1.from)(decoded); // `from` creates an Observable emission from each item (Sample) in the array })); } exports.binaryBufferToSamples = binaryBufferToSamples; /** * @hidden * * Decode the supplied Buffer as a list of Sample. * * Supplied buffer's length must be multiple of * `encodedSampleSize(channelCount)`. * * NB: This method does not guarantee validity of decoded samples. When * supplied with a buffer of appropriate length, it will always return a * matching number of Sample8. Since the encoding protocol defines no * metadata/checksum, correctness must be guaranteed via test coverage. * * @param buffer Buffer with binary payload to decode. * @param channelCount Number of expected channels in each sample. * * @returns List of decoded Samples present in buffer. */ function decode(buffer, channelCount) { let sampleLen = encodedSampleSize(channelCount); // Alternative: relax this check, process sampleLen at a time, discard remainder? if (buffer.length % sampleLen != 0) { throw new Error(`buffer.length (${buffer.length}) for ${channelCount} channels must be multiple of ${sampleLen}B)`); } let sampleCount = buffer.length / sampleLen; let samples = new Array(sampleCount); for (let i = 0; i < sampleCount; i++) { let offset = i * sampleLen; let channelData = new Array(channelCount); // Read 8 bytes for timestamp & advance offset let ts = buffer.readBigUInt64BE(offset); offset += TimestampSize; // Read 1 byte for marker & advance offset let marker = buffer.readUInt16BE(offset); offset += MarkerSize; // Read 8 bytes for each channel & advance offset for (let i = 0; i < channelCount; i++) { channelData[i] = buffer.readDoubleBE(offset); offset += ChannelDataSize; } samples[i] = { timestamp: Number(ts), // TODO: uncomment when ready // marker: marker, data: channelData }; } return samples; } exports.decode = decode; /** * @hidden * * Calculate the size of each sample based on the number of channels. * * Each sample has the following 3 segments: * - Timestamp: 8 bytes (UInt64); contains current time in millis since epoch) * - Marker: 2 bytes (UInt16); for classifier data * - Data: N * 8 bytes (Double), each entry representing data from a different * electrode. * * +-----------+--------+------------------+ * | timestamp | marker | data (e1 ... eN) | * +-----------+--------+------------------+ * * The number of entries for Data varies per hardware model. It can be assumed * to remain constant for the lifetime of the program. */ function encodedSampleSize(channelCount) { return SampleFixedSize + channelCount * ChannelDataSize; } exports.encodedSampleSize = encodedSampleSize;