ixfx
Version:
Bundle of ixfx libraries
1,672 lines (1,671 loc) • 55.3 kB
TypeScript
import { i as Interval } from "./types-1oz6G7XR.js";
import { t as Continuously } from "./continuously-DBwR-cNf.js";
import { D as QueueMutable } from "./index-CvH8hp8B.js";
import { n as SimpleEventEmitter, r as ISimpleEventEmitter } from "./index-DzASKzet.js";
import { bn as Point, fn as Rect } from "./index-CWMm4bvc.js";
import { l as NumberTracker } from "./index-CS8xRat2.js";
import { i as Transitions, n as StateChangeEvent, r as StateMachineWithEvents } from "./state-machine-CTmK75_L.js";
import { n as ManualCapturer } from "./index-AowvMaOR.js";
//#region ../packages/io/src/codec.d.ts
/**
* Handles utf-8 text encoding/decoding
*/
declare class Codec {
enc: TextEncoder;
dec: TextDecoder;
/**
* Convert string to Uint8Array buffer
* @param text
* @returns
*/
toBuffer(text: string): Uint8Array<ArrayBuffer>;
/**
* Returns a string from a provided buffer
* @param buffer
* @returns
*/
fromBuffer(buffer: AllowSharedBufferSource): string;
}
//#endregion
//#region ../packages/io/src/string-receive-buffer.d.ts
/**
* Receives text
*/
declare class StringReceiveBuffer {
private onData;
separator: string;
buffer: string;
stream: WritableStream<string> | undefined;
constructor(onData: (data: string) => void, separator?: string);
close(): Promise<void>;
clear(): void;
writable(): WritableStream<string>;
private createWritable;
addImpl(string_: string): string;
add(string_: string): void;
}
//#endregion
//#region ../packages/io/src/string-write-buffer.d.ts
type Opts$2 = {
readonly chunkSize?: number;
readonly interval?: Interval;
};
/**
* Buffers a queue of strings.
*
* When text is queued via {@link add}, it is chopped up
* into chunks and sent in serial to the `dataHandler` function.
* Data is processed at a set rate, by default 10ms.
*
* ```js
* const dataHandler = (data:string) => {
* // Do something with queued data.
* // eg. send to serial port
* }
*
* // Create a buffer with a chunk size of 100 characters
* const b = new StringWriteBuffer(dataHandler, { chunkSize: 100 });
* b.add('some text'); // Write to buffer
* // dataHandler will be called until queued data is empty
* ```
*
* It's also possible to get the buffer as a WritableStream<string>:
* ```js
* const dataHandler = (data:string) => { ... }
* const b = new StringWriteBuffer(dataHandler, 100);
* const s = b.writable();
* ```
*
* Other functions:
* ```js
* b.close(); // Close buffer
* b.clear(); // Clear queued data, but don't close anything
* ```
*/
declare class StringWriteBuffer {
private dataHandler;
paused: boolean;
queue: QueueMutable<string>;
writer: Continuously;
stream: WritableStream<string> | undefined;
closed: boolean;
chunkSize: number;
/**
* Constructor
* @param dataHandler Calback to 'send' data onwards
* @param opts Options
*/
constructor(dataHandler: (data: string) => Promise<void>, opts?: Opts$2);
/**
* Close writer (async)
*/
close(): Promise<void>;
/**
* Clear queued data.
*
* Throws an error if {@link close} has been called.
*/
clear(): void;
/**
* Gets the buffer as a writable stream.
*
* Do not close stream directly, use .close on this class instead.
*
* Throws an error if .close() has been called.
* @returns Underlying stream
*/
writable(): WritableStream<string>;
private createWritable;
/**
* Run in a `continunously` loop to process queued data
* @returns _False_ if queue is empty and loop should stop. _True_ if it shoud continue.
*/
onWrite(): Promise<boolean>;
/**
* Returns _true_ if {@link close} has been called.
*/
get isClosed(): boolean;
/**
* Adds some queued data to send.
* Longer strings are automatically chunked up according to the buffer's settings.
*
* Throws an error if {@link close} has been called.
* @param stringToQueue
*/
add(stringToQueue: string): void;
}
//#endregion
//#region ../packages/io/src/generic-state-transitions.d.ts
declare const genericStateTransitionsInstance: {
readonly ready: "connecting";
readonly connecting: readonly ["connected", "closed"];
readonly connected: readonly ["closed"];
readonly closed: "connecting";
};
//#endregion
//#region ../packages/io/src/types.d.ts
type IoDataEvent = {
readonly data: string;
};
type IoEvents<StateMachineTransitions extends Transitions> = {
readonly data: IoDataEvent;
readonly change: StateChangeEvent<StateMachineTransitions>;
};
type GenericStateTransitions = Readonly<typeof genericStateTransitionsInstance>;
type BleDeviceOptions = {
readonly service: string;
readonly rxGattCharacteristic: string;
readonly txGattCharacteristic: string;
readonly chunkSize: number;
readonly name: string;
readonly connectAttempts: number;
readonly debug: boolean;
};
type FrameProcessorSources = `` | `camera` | `video`;
//#endregion
//#region ../packages/io/src/ble-device.d.ts
declare class BleDevice extends SimpleEventEmitter<IoEvents<GenericStateTransitions>> {
private device;
private config;
states: StateMachineWithEvents<GenericStateTransitions>;
codec: Codec;
rx: BluetoothRemoteGATTCharacteristic | undefined;
tx: BluetoothRemoteGATTCharacteristic | undefined;
gatt: BluetoothRemoteGATTServer | undefined;
verboseLogging: boolean;
rxBuffer: StringReceiveBuffer;
txBuffer: StringWriteBuffer;
constructor(device: BluetoothDevice, config: BleDeviceOptions);
get isConnected(): boolean;
get isClosed(): boolean;
write(txt: string): void;
private writeInternal;
disconnect(): void;
connect(): Promise<void>;
private onRx;
protected verbose(m: string): void;
protected log(m: string): void;
protected warn(m: unknown): void;
}
declare namespace nordic_ble_device_d_exports {
export { NordicBleDevice, Opts$1 as Opts, defaultOpts };
}
declare const defaultOpts: {
chunkSize: number;
service: string;
txGattCharacteristic: string;
rxGattCharacteristic: string;
name: string;
connectAttempts: number;
debug: boolean;
};
type Opts$1 = {
readonly chunkSize?: number;
readonly name?: string;
readonly connectAttempts?: number;
readonly debug?: boolean;
};
declare class NordicBleDevice extends BleDevice {
constructor(device: BluetoothDevice, opts?: Opts$1);
}
//#endregion
//#region ../packages/io/src/audio/visualiser.d.ts
declare class AudioVisualiser {
freqMaxRange: number;
audio: AudioAnalyser;
parent: HTMLElement;
lastPointer: Point;
pointerDown: boolean;
pointerClicking: boolean;
pointerClickDelayMs: number;
pointerDelaying: boolean;
waveTracker: NumberTracker;
freqTracker: NumberTracker;
el: HTMLElement;
constructor(parentElement: HTMLElement, audio: AudioAnalyser);
renderFreq(freq: readonly number[]): void;
isExpanded(): boolean;
setExpanded(value: boolean): void;
clear(): void;
clearCanvas(canvas: HTMLCanvasElement | null): void;
renderWave(wave: readonly number[], bipolar?: boolean): void;
getPointerRelativeTo(elem: HTMLElement): {
x: number;
y: number;
};
onPointer(event: MouseEvent | PointerEvent): void;
}
//#endregion
//#region ../packages/io/src/audio/analyser.d.ts
/**
* Options for audio processing
*
* fftSize: Must be a power of 2, from 32 - 32768. Higher number means
* more precision and higher CPU overhead
* @see https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/fftSize
*
* smoothingTimeConstant: Range from 0-1, default is 0.8.
* @see https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/smoothingTimeConstant
*
* debug: If true, additonal console logging will happen
*/
type Opts = {
readonly showVis?: boolean;
/**
* FFT size. Must be a power of 2, from 32 - 32768. Higher number means
* more precision and higher CPU overhead
* @see https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/fftSize
*/
readonly fftSize?: number;
/**
* Range from 0-1, default is 0.8
* @see https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/smoothingTimeConstant
*/
readonly smoothingTimeConstant?: number;
readonly debug?: boolean;
};
type DataAnalyser = (node: AnalyserNode, analyser: AudioAnalyser) => void;
/**
* Basic audio analyser. Returns back waveform and FFT analysis. Use {@link analyserPeakLevel} if you want sound level, or {@link analyserFrequency} if you just want FFT results.
*
* ```js
* const onData = (freq, wave, analyser) => {
* // Demo: Get FFT results just for 100Hz-1KHz.
* const freqSlice = analyser.sliceByFrequency(100,1000,freq);
*
* // Demo: Get FFT value for a particular frequency (1KHz)
* const amt = freq[analyser.getIndexForFrequency(1000)];
* }
* analyserBasic(onData, {fftSize: 512});
* ```
*
* An `Analyser` instance is returned and can be controlled:
* ```js
* const analyser = analyserBasic(onData);
* analyser.paused = true;
* ```
*
* Note: Browers won't allow microphone access unless the call has come from a user-interaction, eg pointerup event handler.
*
* @param onData Handler for data
* @param opts Options
* @returns Analyser instance
*/
declare const analyserBasic: (onData: (freq: Float32Array, wave: Float32Array, analyser: AudioAnalyser) => void, opts?: Opts) => AudioAnalyser;
/**
* Basic audio analyser. Returns FFT analysis. Use {@link analyserPeakLevel} if you want the sound level, or {@link analyserBasic} if you also want the waveform.
*
* ```js
* const onData = (freq, analyser) => {
* // Demo: Print out each sound frequency (Hz) and amount of energy in that band
* for (let i=0;i<freq.length;i++) {
* const f = analyser.getFrequencyAtIndex(0);
* console.log(`${i}. frequency: ${f} amount: ${freq[i]}`);
* }
* }
* analyserFrequency(onData, {fftSize:512});
* ```
*
* Note: Browers won't allow microphone access unless the call has come from a user-interaction, eg pointerup event handler.
*
* @param onData
* @param opts
* @returns
*/
declare const analyserFrequency: (onData: (freq: Float32Array, analyser: AudioAnalyser) => void, opts?: Opts) => AudioAnalyser;
/**
* Basic audio analyser which reports the peak sound level.
*
* ```js
* analyserPeakLevel(level => {
* console.log(level);
* });
* ```
*
* Note: Browers won't allow microphone access unless the call has come from a user-interaction, eg pointerup event handler.
* @param onData
* @param opts
* @returns
*/
declare const analyserPeakLevel: (onData: (level: number, analyser: AudioAnalyser) => void, opts?: Opts) => AudioAnalyser;
/**
* Helper for doing audio analysis. It takes case of connecting the audio stream, running in a loop and pause capability.
*
* Provide a function which works with an [AnalyserNode](https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode), and does something with the result.
* ```js
* const myAnalysis = (node, analyser) => {
* const freq = new Float32Array(node.frequencyBinCount);
* node.getFloatFrequencyData(freq);
* // Do something with frequency data...
* }
* const a = new Analyser(myAnalysis);
* ```
*
* Helper functions provide ready-to-use Analysers:
* * {@link analyserPeakLevel} peak decibel reading
* * {@link analyserFrequency} FFT results
* * {@link analyserBasic} FFT results and waveform
*
* Note: Browers won't allow microphone access unless the call has come from a user-interaction, eg pointerup event handler.
*
*/
declare class AudioAnalyser {
#private;
showVis: boolean;
fftSize: number;
smoothingTimeConstant: number;
debug: boolean;
visualiser: AudioVisualiser | undefined;
audioCtx: AudioContext | undefined;
analyserNode: AnalyserNode | undefined;
analyse: DataAnalyser;
constructor(analyse: DataAnalyser, opts?: Opts);
init(): void;
get paused(): boolean;
set paused(v: boolean);
private setup;
private onMicSuccess;
private analyseLoop;
/**
* Returns the maximum FFT value within the given frequency range
*/
getFrequencyRangeMax(lowFreq: number, highFreq: number, freqData: readonly number[]): number;
/**
* Returns a sub-sampling of frequency analysis data that falls between
* `lowFreq` and `highFreq`.
* @param lowFreq Low frequency
* @param highFreq High frequency
* @param freqData Full-spectrum frequency data
* @returns Sub-sampling of analysis
*/
sliceByFrequency(lowFreq: number, highFreq: number, freqData: readonly number[]): number[];
/**
* Returns the starting frequency for a given binned frequency index.
* @param index Array index
* @returns Sound frequency
*/
getFrequencyAtIndex(index: number): number;
/**
* Returns a binned array index for a given frequency
* @param freq Sound frequency
* @returns Array index into frequency bins
*/
getIndexForFrequency(freq: number): number;
}
//#endregion
//#region ../packages/io/src/audio/types.d.ts
type AudioOscillatorOptions = {
type: OscillatorType;
frequency: number;
id: string;
};
type BasicAudio = {
ctx: AudioContext;
pan: StereoPannerNode;
gain: GainNode;
filter: BiquadFilterNode;
id: string;
};
type BasicAudioElement = BasicAudio & {
el: HTMLMediaElement;
};
type BasicAudioOscillator = BasicAudio & {
osc: OscillatorNode;
};
//#endregion
//#region ../packages/io/src/audio/from-audio-element.d.ts
/**
* Scans page for <AUDIO> elements and creates playable controllers for them.
* It uses the element's 'id' attribute as a way of fetching one later.
*
* ```js
* const ae = new AudioElements();
* ae.init(); // Initialise
*
* const a = ae.get('kick'); // Get the source that had id 'kick'
* ```
*/
declare class AudioElements {
#private;
filterType: BiquadFilterType;
constructor();
init(): void;
/**
* Gets a BasicAudio instance by key
* @param key
* @returns BasicAudio instance, or undefined
*/
get(key: string): BasicAudioElement | undefined;
}
/**
* Create a BasicAudioElement instance from an <AUDIO> tag in the HTML document.
*
* See {@link AudioElements} to automatically create sources from all <AUDIO> elements.
* @param audioElementOrQuery Element or query (eg '#some-id')
* @param filterType Filter type. Defaults to 'lowpass'
* @returns
*/
declare function createFromAudioElement(audioElementOrQuery: HTMLMediaElement | string, filterType?: BiquadFilterType): BasicAudioElement;
//#endregion
//#region ../packages/io/src/audio/from-oscillator.d.ts
/**
* Initialise audio with an oscillator source
* @param oscillatorOptions
* @returns BasicAudio instance
*/
declare function createOscillator(oscillatorOptions?: Partial<AudioOscillatorOptions>): BasicAudioOscillator;
declare namespace index_d_exports$2 {
export { AudioAnalyser, AudioElements, AudioOscillatorOptions, AudioVisualiser, BasicAudio, BasicAudioElement, BasicAudioOscillator, DataAnalyser, Opts, analyserBasic, analyserFrequency, analyserPeakLevel, createFromAudioElement, createOscillator };
}
//#endregion
//#region ../packages/io/src/midi/types.d.ts
type MidiCommands = 'noteon' | 'noteoff' | 'pitchbend' | 'cc' | 'poly-at' | 'progchange' | 'at';
type MidiMessage = {
command: MidiCommands;
channel: number;
note: number;
velocity: number;
};
type NoteMidiMessage = MidiMessage & {
command: `noteon` | `noteoff`;
noteName: string;
frequency: number;
};
//#endregion
//#region ../packages/io/src/midi/midi-fns.d.ts
/**
* Sends a note on and note off
* @param port
* @param channel
* @param note
* @param velocity
* @param duration
* @param delay
*/
declare const sendNote: (port: MIDIOutput, channel: number, note: number, velocity: number, duration?: number, delay?: DOMHighResTimeStamp) => void;
/**
* Parses MIDI data from an array into a MidiMessage
*
* ```js
* function onMidiMessage(event: MIDIMessageEvent) {
* const msg = unpack(event.data);
* // { command, channel, note, velocity }
* }
*
* // Where 'input' is a MIDIInput
* input.addEventListener(`midimessage`, onMidiMessage);
* ```
* @param data
* @returns
*/
declare const unpack: (data: Uint8Array) => MidiMessage;
/**
* Packs a MidiMessage into an array for sending to a MIDIOutput.
*
* ```js
* const msg: Midi.MidiMessage = {
* command: `cc`,
* channel: 1,
* velocity: 50,
* note: 40
* }
*
* // Where 'output' is a MIDIOutput
* output.send(pack(msg));
* ```
* @param message
* @returns
*/
declare const pack: (message: MidiMessage) => number[];
//#endregion
//#region ../packages/io/src/midi/manager.d.ts
type MidiManagerState = {
initialised: boolean;
errorReason: string;
};
type MidiManagerEvents = {
open: {
port: MIDIPort;
};
close: {
port: MIDIPort;
};
deviceConnected: {
port: MIDIPort;
};
deviceDisconnected: {
port: MIDIPort;
};
message: (MidiMessage | NoteMidiMessage) & {
port: MIDIInput;
raw: Uint8Array;
};
};
/**
* Midi Manager makes simplifies connecting to ports
* and having omni input/output
*
* ```js
* const midi = new MidiManager(); // By default connects to all ins and outs
* midi.addEventListener(`message`, event => {
* // Do something with received MIDI data from any input
* })
*
* midi.scan();
* midi.send({ command: `cc`, note: 10, channel: 1, velocity: 20 });
*
* // Sends note '10' on channel 1, velocity 100, duration 200ms
* midi.sendNote(1, 10, 100, 200);
* ```
* Events:
* * open/close: Connected port is open/closed
* * deviceConnected/deviceDisconnected: A port is newly available or unavailable
* * message: MIDI event received
*/
declare class MidiManager extends SimpleEventEmitter<MidiManagerEvents> {
#private;
verbose: boolean;
constructor();
getInUse(): Generator<MIDIPort, void, unknown>;
getInUseInput(): Generator<MIDIInput, void, unknown>;
getInUseOutput(): Generator<MIDIOutput, void, unknown>;
known(): Generator<MIDIPort, void, unknown>;
knownInput(): Generator<MIDIInput, void, unknown>;
knownOutput(): Generator<MIDIOutput, void, unknown>;
scan(): Promise<void>;
/**
* Sends a message to a port.
*
* If port is omitted, all open output ports are used.
* @param message
* @param port
* @param timestamp
*/
send(message: MidiMessage, port?: MIDIOutput, timestamp?: DOMHighResTimeStamp): void;
sendNote(channel: number, note: number, velocity: number, duration: number, delay?: DOMHighResTimeStamp, port?: MIDIOutput): void;
closeAll(what?: `input` | `output` | `both`): Promise<void>;
setOmniInput(value: boolean): Promise<void>;
get omniInput(): boolean;
setOmniOutput(value: boolean): Promise<void>;
get omniOutput(): boolean;
dumpToStringLines(): string[];
/**
* Opens `port`.
*
* If `exclusive` is _true_, all other ports of that type
* (ie. input or output) are closed before the port is opened.
*
* If `exclusive` is _false_ (default), already open ports are left open.
* @param port
* @param exclusive
*/
open(port: MIDIPort, exclusive?: boolean): Promise<void>;
findKnownPort(fn: (p: MIDIPort) => boolean): MIDIPort | undefined;
filterKnownPort(fn: (p: MIDIPort) => boolean): Generator<MIDIPort, void, unknown>;
findInUsePort(fn: (p: MIDIPort) => boolean): MIDIPort | undefined;
filterInUsePort(fn: (p: MIDIPort) => boolean): Generator<MIDIPort, void, unknown>;
}
//#endregion
//#region ../packages/io/src/midi/control.d.ts
type ControlEvents = {
change: {
velocity: number;
velocityScaled: number;
control: Control;
};
};
declare class Feedback {
channel: number;
cc: number;
note: number;
output?: MIDIOutput;
portName?: string;
constructor(options?: Partial<{
channel: number;
cc: number;
note: number;
output: MIDIOutput;
portName: string;
}>);
setOutputPort(port: MIDIPort): boolean;
sendRaw(value: number): boolean;
}
declare class Control extends SimpleEventEmitter<ControlEvents> {
#private;
static controlCount: number;
inputChannel: number;
inputCommand?: MidiCommands;
inputNote: number;
inputVelocityScale: readonly [0, 127];
feedbackChannel: number;
feedbackCommand?: MidiCommands;
feedbackNote: number;
feedbackVelocity: number;
name: string;
lastMessage?: MidiMessage;
onInputMessage(message: MidiMessage): boolean;
get scaledVelocity(): number;
}
//#endregion
//#region ../packages/io/src/midi/midi-fighter.d.ts
/**
* Events fired by a {@link MidiFighter}} instance
*/
type MidiFighterEvents = {
/**
* Virtual bank has changed
*/
bankChange: {
prev: number;
current: number;
mf: MidiFighter;
implicit: boolean;
};
/**
* A side button has been pressed
*/
sideButton: {
position: `top` | `bottom`;
side: `left` | `right`;
bank: number;
mf: MidiFighter;
};
/**
* An encoder has been pressed
*/
switch: {
previous: number;
encoder: MidiFighterEncoder;
value: number;
};
/**
* An encoder has been changed
*/
encoder: {
previous: number;
encoder: MidiFighterEncoder;
value: number;
};
/**
* Connection state changed
*/
state: {
previous: MidiFighterState;
state: MidiFighterState;
mf: MidiFighter;
};
};
/**
* Events of a {@link MidiFighterEncoder}
*/
type MidiFighterEncoderEvents = {
switch: {
previous: number;
encoder: MidiFighterEncoder;
value: number;
};
encoder: {
previous: number;
encoder: MidiFighterEncoder;
value: number;
};
};
/**
* States for a {@link MidiFighter} instance
*/
type MidiFighterState = `ready` | `disconnected`;
/**
* Connects to a DJ Tech Tools Midi Fighter controller.
*
* Use the 'state' event and wait for state to be 'ready'.
*
* ```js
* const mf = new MidiFighter();
* mf.addEventListener(`state`, event => {
* if (event.state === `ready`) {
* // Can work with device now
* mf.bank = 1;
* }
* });
* mf.addEventListener(`encoder`, event => {
* // Do something with encoder value
* });
* mf.setPort(someMidiInputPort);
* mf.setPort(someMidiOutputPort);
* ```
* Assumes default settings are loaded on the controller
*
* Supports
* * Listening for encoder moves and button presses
* * Changing colour pip below each encoder
* * Setting LED bar for each encoder
* * Changing banks, or detecting when the user has done so via the physical buttons
*
* Events:
* * bankChange: Current bank has changed
* * sideButton: Side button pressed
* * switch: Encoder has been pressed
* * encoder: Encoder has been moved
* * state: Midi Fighter has both input/output ports or not.
*/
declare class MidiFighter extends SimpleEventEmitter<MidiFighterEvents> {
#private;
readonly encoders: MidiFighterEncoder[];
/**
* If true, messages sent to Midi Fighter are printed to console
*/
logOutgoing: boolean;
/**
* Channel bank change events are received on
*/
bankChangeChannel: number;
/**
* Channel side button press events are received on
*/
sideButtonChannel: number;
constructor();
/**
* Sets a port for this instance to use.
* This will need to be called separately for the input and output ports
* @param port
*/
setPort(port: MIDIPort): void;
/**
* Sets the current bank (1..4)
*
* Triggers `bankChange` event.
*/
set bank(bank: number);
/**
* Gets the current bank number (1-4)
*/
get bank(): number;
/**
* Yields all encooders within the specified bank number.
* If no bank number is given, current bank is used
* @param bank
*/
getBank(bank?: number): Generator<MidiFighterEncoder, void, unknown>;
/**
* Gets an encoder by its index and bank. If no bank is specified,
* the current is used.
*
* ```js
* mf.getEncoder(4); // Get encoder #4 on current bank
* mf.getEncoder(4, 2); // Get encoder #4 from bank #2
* ```
* @param encoder Encoder number (1..16)
* @param bank Bank number (1..4)
* @returns Encoder
*/
getEncoder(encoder: number, bank?: number): MidiFighterEncoder | undefined;
/**
* Sends a message to the output port associated with this instance.
* If there's no output port, message is dropped and _false_ returned.
* @param message
*/
send(message: MidiMessage): boolean;
/**
* Gets the current output port
*/
get outputPort(): MIDIOutput | undefined;
/**
* Gets the current input port
*/
get inputPort(): MIDIInput | undefined;
/**
* Returns the current state
*/
get state(): MidiFighterState;
}
/**
* Represents a single encoder.
*/
declare class MidiFighterEncoder extends SimpleEventEmitter<MidiFighterEncoderEvents> {
readonly mf: MidiFighter;
/**
* Bank (1..4) of encoder
*/
readonly bank: number;
/**
* Encoder index (1..16)
*/
readonly encoder: number;
/**
* Note/CC for received encoder values
*/
inputEncoderNoteOrCc: number;
/**
* Midi channel for received encoder values
*/
inputEncoderChannel: number;
/**
* Midi channel for received switch values
*/
inputSwitchChannel: number;
/**
* Note/CC for received switch values
*/
inputSwitchNoteOrCc: number;
/**
* Channel to change LED effect (eg strobe)
*/
ledEffectChannel: number;
/**
* Channel to change LED colour
*/
ledColourChannel: number;
/**
* Channel to change LED ring value
*/
ledRingChannel: number;
/**
* Note for this encoder.
*/
encoderStaticNote: number;
/**
* The last encoder value received
*/
lastEncoderValue: number;
/**
* The last switch value received
*/
lastSwitchValue: number;
/**
* Do not create yourself. Access via a {@link MidiFighter} instance.
* @private
* @param mf
* @param options
*/
constructor(mf: MidiFighter, options: {
bank: number;
encoder: number;
});
/**
* Called by a {@link MidiFighter} instance when a value is received associated with this encoder.
* Do not call directly
* @private
* @param value
*/
onValueSet(value: number): void;
/**
* Called by a {@link MidiFighter} instance when the switch value for this encoder changes
* @private
* @param value
*/
onSwitchSet(value: number): void;
/**
* Set a scalar LED ring value (0..1).
*
* ```js
* encoder.setLedRing(0.5); // Set to 50%
* ```
* Use {@link setLedRingRaw} to set 0..127 integer value
* @param v Scalar (0..1)
*/
setLedRing(v: number): void;
/**
* Sets the raw (0..127) value for the LED ring feedback. Use {@link setLedRing} for scalar values (0..1)
*
* ```js
* encoder.setLedRingRaw(50);
* ```
*
* @param v Raw value (0..127)
*/
setLedRingRaw(v: number): void;
/**
* Sets the switch colour based on a 0..1 standard hue degree
* ```js
* const hsl = Colour.HslSpace.fromCss(`orange`);
* encoder.setSwitchColourHue(hsl.hue);
* ```
* @param v Hue degree (0..1) range
*/
setSwitchColourHue(v: number): void;
/**
* Set the switch colour based on 0..127 Midi Fighter range (start/end in blue).
* Use {@link setSwitchColourHue} to set colour based on hue angle instead
*
* See page 4 of the MF manual.
* @param v
* @returns
*/
setSwitchColourRaw(v: number): void;
/**
* Set the effect of the colour pip
* ```js
* encoder.setSwitchEffect(`strobe`, 3);
* ```
* @param kind
* @param value
*/
setSwitchEffect(kind: `none` | `strobe` | `pulse` | `rainbow`, value?: number): void;
}
//#endregion
//#region ../packages/io/src/midi/notes.d.ts
type ParsedNote = [noteNumber: number, name: string, frequency: number];
declare const getParsedNotes: () => ParsedNote[];
declare const noteNameToNumber: (name: string) => number;
declare const noteNameToFrequency: (name: string) => number;
declare const noteNumberToName: (number: number) => string;
declare const noteNumberToFrequency: (number: number) => number;
declare namespace index_d_exports$1 {
export { Control, ControlEvents, Feedback, MidiCommands, MidiFighter, MidiFighterEncoder, MidiFighterEncoderEvents, MidiFighterEvents, MidiFighterState, MidiManager, MidiManagerEvents, MidiManagerState, MidiMessage, NoteMidiMessage, ParsedNote, getParsedNotes, noteNameToFrequency, noteNameToNumber, noteNumberToFrequency, noteNumberToName, pack, sendNote, unpack };
}
//#endregion
//#region ../packages/io/src/espruino-ble-device.d.ts
/**
* An Espruino BLE-connection
*
* See [online demos](https://demos.ixfx.fun/io/)
*
* Use the `puck` function to initialise and connect to a Puck.js.
* It must be called in a UI event handler for browser security reasons.
*
* ```js
* const e = await Espruino.puck();
* ```
*
* To connect to a particular device:
*
* ```js
* const e = await Espruino.puck({name:`Puck.js a123`});
* ```
*
* Listen for events:
* ```js
* // Received something
* e.addEventListener(`data`, d => console.log(d.data));
* // Monitor connection state
* e.addEventListener(`change`, c => console.log(`${d.priorState} -> ${d.newState}`));
* ```
*
* Write to the device (note the \n for a new line at the end of the string). This will
* execute the code on the Espruino.
*
* ```js
* e.write(`digitalPulse(LED1,1,[10,500,10,500,10]);\n`);
* ```
*
* Run some code and return result:
* ```js
* const result = await e.eval(`2+2\n`);
* ```
*/
declare class EspruinoBleDevice extends NordicBleDevice {
evalTimeoutMs: number;
evalReplyBluetooth: boolean;
/**
* Creates instance. You probably would rather use {@link puck} to create.
* @param device
* @param opts
*/
constructor(device: BluetoothDevice, opts?: Options);
/**
* Writes a script to Espruino.
*
* It will first send a CTRL+C to cancel any previous input, `reset()` to clear the board,
* and then the provided `code` followed by a new line.
*
* Use {@link eval} instead to execute remote code and get the result back.
*
* ```js
* // Eg from https://www.espruino.com/Web+Bluetooth
* writeScript(`
* setInterval(() => Bluetooth.println(E.getTemperature()), 1000);
* NRF.on('disconnect',()=>reset());
* `);
* ```
*
* @param code Code to send. A new line is added automatically.
*/
writeScript(code: string): Promise<void>;
/**
* Sends some code to be executed on the Espruino. The result
* is packaged into JSON and sent back to your code. An exception is
* thrown if code can't be executed for some reason.
*
* ```js
* const sum = await e.eval(`2+2`);
* ```
*
* It will wait for a period of time for a well-formed response from the
* Espruino. This might not happen if there is a connection problem
* or a syntax error in the code being evaled. In cases like the latter,
* it will take up to `timeoutMs` (default 5 seconds) before we give up
* waiting for a correct response and throw an error.
*
* Tweaking of the timeout may be required if `eval()` is giving up too quickly
* or too slowly. A default timeout can be given when creating the class.
*
* Options:
* timeoutMs: Timeout for execution. 5 seconds by default
* assumeExclusive If true, eval assumes all replies from controller are in response to eval. True by default
* debug: If true, execution is traced via `warn` callback
* @param code Code to run on the Espruino.
* @param opts Options
* @param warn Function to pass warning/trace messages to. If undefined, this.warn is used, printing to console.
*/
eval(code: string, opts?: EvalOpts, warn?: (message: string) => void): Promise<string>;
}
//#endregion
//#region ../packages/io/src/json-device.d.ts
/**
* Options for JsonDevice
*/
type JsonDeviceOpts = {
/**
* How much data to transfer at a time
*/
readonly chunkSize?: number;
/**
* Name of device. This is only used for assisting the console.log output
*/
readonly name?: string;
/**
* Number of times to automatically try to reconnect
*/
readonly connectAttempts?: number;
/**
* If true, additional logging will be done
*/
readonly debug?: boolean;
};
/**
* Data received event
*/
type JsonDataEvent = {
/**
* Data received
*/
readonly data: string;
};
/**
* Events emitted by JsonDevice
*/
type JsonDeviceEvents = {
/**
* Data received
*/
readonly data: JsonDataEvent;
/**
* State changed
*/
readonly change: StateChangeEvent<GenericStateTransitions>;
};
declare abstract class JsonDevice extends SimpleEventEmitter<JsonDeviceEvents> {
states: StateMachineWithEvents<GenericStateTransitions>;
codec: Codec;
verboseLogging: boolean;
name: string;
connectAttempts: number;
chunkSize: number;
rxBuffer: StringReceiveBuffer;
txBuffer: StringWriteBuffer;
constructor(config?: JsonDeviceOpts);
get isConnected(): boolean;
get isClosed(): boolean;
write(txt: string): void;
/**
* Writes text to output device
* @param txt
*/
protected abstract writeInternal(txt: string): Promise<void>;
close(): Promise<void>;
/**
* Must change state
*/
abstract onClosed(): void;
abstract onPreConnect(): Promise<void>;
connect(): Promise<void>;
/**
* Should throw if did not succeed.
*/
protected abstract onConnectAttempt(): Promise<void>;
private onRx;
protected verbose(m: string): void;
protected log(m: string): void;
protected warn(m: unknown): void;
}
declare namespace serial_d_exports {
export { Device, JsonDataEvent, JsonDeviceEvents, JsonDeviceOpts, SerialOpts };
}
type SerialOpts = JsonDeviceOpts & {
readonly filters?: readonly SerialPortFilter[];
readonly baudRate?: number;
/**
* End-of-line string sequence. \r\n by default.
*/
readonly eol?: string;
};
/**
* Serial device. Assumes data is sent with new line characters (\r\n) between messages.
*
* ```
* import { Serial } from '@ixfx/io.js'
* const s = new Serial.Device();
* s.addEventListener(`change`, evt => {
* console.log(`State change ${evt.priorState} -> ${evt.newState}`);
* if (evt.newState === `connected`) {
* // Do something when connected...
* }
* });
*
* // In a UI event handler...
* s.connect();
* ```
*
* Reading incoming data:
* ```
* // Parse incoming data as JSON
* s.addEventListener(`data`, evt => {
* try {
* const o = JSON.parse(evt.data);
* // If we get this far, JSON is legit
* } catch (ex) {
* }
* });
* ```
*
* Writing to the microcontroller
* ```
* s.write(JSON.stringify({msg:"hello"}));
* ```
*/
declare class Device extends JsonDevice {
private config;
port: SerialPort | undefined;
tx: WritableStreamDefaultWriter<string> | undefined;
abort: AbortController;
baudRate: number;
constructor(config?: SerialOpts);
/**
* Writes text to the underlying output
* @param txt
*/
protected writeInternal(txt: string): Promise<void>;
onClosed(): void;
onPreConnect(): Promise<void>;
onConnectAttempt(): Promise<void>;
}
//#endregion
//#region ../packages/io/src/espruino-serial-device.d.ts
type EspruinoSerialDeviceOpts = SerialOpts & {
readonly evalTimeoutMs?: number;
};
declare class EspruinoSerialDevice extends Device {
evalTimeoutMs: number;
evalReplyBluetooth: boolean;
constructor(opts?: EspruinoSerialDeviceOpts);
disconnect(): Promise<void>;
/**
* Writes a script to Espruino.
*
* It will first send a CTRL+C to cancel any previous input, `reset()` to clear the board,
* and then the provided `code` followed by a new line.
*
* Use {@link eval} instead to execute remote code and get the result back.
*
* ```js
* // Eg from https://www.espruino.com/Web+Bluetooth
* writeScript(`
* setInterval(() => Bluetooth.println(E.getTemperature()), 1000);
* NRF.on('disconnect',()=>reset());
* `);
* ```
*
* @param code Code to send. A new line is added automatically.
*/
writeScript(code: string): void;
/**
* Sends some code to be executed on the Espruino. The result
* is packaged into JSON and sent back to your code. An exception is
* thrown if code can't be executed for some reason.
*
* ```js
* const sum = await e.eval(`2+2`);
* ```
*
* It will wait for a period of time for a well-formed response from the
* Espruino. This might not happen if there is a connection problem
* or a syntax error in the code being evaled. In cases like the latter,
* it will take up to `timeoutMs` (default 5 seconds) before we give up
* waiting for a correct response and throw an error.
*
* Tweaking of the timeout may be required if `eval()` is giving up too quickly
* or too slowly. A default timeout can be given when creating the class.
*
* Options:
* timeoutMs: Timeout for execution. 5 seconds by default
* assumeExclusive: If true, eval assumes all replies from controller are in response to eval. True by default
* debug: If true, execution is traced via `warn` callback
* @param code Code to run on the Espruino.
* @param opts Options
* @param warn Function to pass warning/trace messages to. If undefined, this.warn is used, printing to console.
*/
eval(code: string, opts?: EvalOpts, warn?: (message: string) => void): Promise<string>;
}
declare namespace espruino_d_exports {
export { EspruinoBleDevice, EspruinoBleOpts, EspruinoDevice, EspruinoSerialDevice, EspruinoSerialDeviceOpts, EspruinoStates, EvalOpts, Events, Options, bangle, connectBle, deviceEval, puck, serial };
}
type EspruinoStates = `ready` | `connecting` | `connected` | `closed` | `closing`;
/**
* Options for device
*/
type Options = {
/**
* Default milliseconds to wait before giving up on a well-formed reply. 5 seconds is the default.
*/
readonly evalTimeoutMs?: number;
/**
* Name of device. Only used for printing log mesages to the console
*/
readonly name?: string;
/**
* If true, additional logging information is printed
*/
readonly debug?: boolean;
};
/**
* Options for code evaluation
*/
type EvalOpts = {
/**
* Milliseconds to wait before giving up on well-formed reply. 5 seconds is the default.
*/
readonly timeoutMs?: number;
/**
* If true (default), it assumes that anything received from the board
* is a response to the eval
*/
readonly assumeExclusive?: boolean;
/**
* If true, executed code is traced
*/
readonly debug?: boolean;
};
type EspruinoBleOpts = {
/**
* If the name is specified, this value is used
* for filtering Bluetooth devices
*/
readonly name?: string;
/**
* If true, additional logging messages are
* displayed on the console
*/
readonly debug?: boolean;
/**
* If specified, these filtering options are used instead
*/
readonly filters?: readonly BluetoothLEScanFilter[];
};
/**
* Instantiates a Puck.js. See {@link EspruinoBleDevice} for more info.
* [Online demos](https://demos.ixfx.fun/io/)
*
* If `opts.name` is specified, this will the the Bluetooth device sought.
*
* ```js
* import { Espruino } from '@ixfx/io.js'
* const e = await Espruino.puck({ name:`Puck.js a123` });
* ```
*
* If no name is specified, a list of all devices starting with `Puck.js` are shown.
*
* To get more control over filtering, pass in `opts.filter`. `opts.name` is not used as a filter in this scenario.
*
* ```js
* import { Espruino } from '@ixfx/io.js'
* const filters = [
* { namePrefix: `Puck.js` },
* { namePrefix: `Pixl.js` },
* {services: [NordicDefaults.service] }
* ]
* const e = await Espruino.puck({ filters });
* ```
*
* @returns Returns a connected instance, or throws exception if user cancelled or could not connect.
*/
declare const puck: (opts?: EspruinoBleOpts) => Promise<EspruinoBleDevice>;
declare const bangle: (opts?: EspruinoBleOpts) => Promise<EspruinoBleDevice>;
/**
* Create a serial-connected Espruino device.
*
* ```js
* import { Espruino } from '@ixfx/io.js'
* const e = await Espruio.serial();
* e.connect();
* ```
*
* Options:
* ```js
* import { Espruino } from '@ixfx/io.js'
* const e = await Espruino.serial({ debug: true, evalTimeoutMs: 1000, name: `My Pico` });
* e.connect();
* ```
*
* Listen for events:
* ```js
* e.addEventListener(`change`, evt => {
* console.log(`State change ${evt.priorState} -> ${evt.newState}`);
* if (evt.newState === `connected`) {
* // Do something when connected...
* }
* });
* ```
*
* Reading incoming data:
* ```
* // Parse incoming data as JSON
* s.addEventListener(`data`, evt => {
* try {
* const o = JSON.parse(evt.data);
* // If we get this far, JSON is legit
* } catch (ex) {
* }
* });
* ```
*
* Writing to the microcontroller
* ```
* s.write(JSON.stringify({msg:"hello"}));
* ```
* @param opts
* @returns Returns a connected instance, or throws exception if user cancelled or could not connect.
*/
declare const serial: (opts?: {
readonly name?: string;
readonly debug?: boolean;
readonly evalTimeoutMs?: number;
}) => Promise<EspruinoSerialDevice>;
/**
* Connects to a generic Espruino BLE device. See {@link EspruinoBleDevice} for more info.
* Use {@link puck} if you're connecting to a Puck.js
*
* If `opts.name` is specified, only this BLE device will be shown.
* ```js
* const e = await connectBle({ name: `Puck.js a123` });
* ```
*
* `opts.filters` overrides and sets arbitary filters.
*
* ```js
* import { Espruino } from '@ixfx/io.js'
* const filters = [
* { namePrefix: `Puck.js` },
* { namePrefix: `Pixl.js` },
* {services: [NordicDefaults.service] }
* ]
* const e = await Espruino.connectBle({ filters });
* ```
*
* @returns Returns a connected instance, or throws exception if user cancelled or could not connect.
*/
declare const connectBle: (opts?: EspruinoBleOpts) => Promise<EspruinoBleDevice>;
type Events = IoEvents<GenericStateTransitions>;
/**
* EspruinoDevice
*
* This base interface is implemented by {@link EspruinoBleDevice} and {@link EspruinoSerialDevice}.
*/
type EspruinoDevice = {
/**
* Sends some code to be executed on the Espruino. The result
* is packaged into JSON and sent back to your code. An exception is
* thrown if code can't be executed for some reason.
*
* ```js
* const sum = await e.eval(`2+2`);
* ```
*
* It will wait for a period of time for a well-formed response from the
* Espruino. This might not happen if there is a connection problem
* or a syntax error in the code being evaled. In cases like the latter,
* it will take up to `timeoutMs` (default 5 seconds) before we give up
* waiting for a correct response and throw an error.
*
* Tweaking of the timeout may be required if `eval()` is giving up too quickly
* or too slowly. A default timeout can be given when creating the class.
*
* Options:
* timeoutMs: Timeout for execution. 5 seconds by default
* assumeExclusive If true, eval assumes all replies from controller are in response to eval. True by default
* debug: If true, execution is traced via `warn` callback
* @param code Code to run on the Espruino.
* @param opts Options
* @param warn Function to pass warning/trace messages to. If undefined, this.warn is used, printing to console.
*/
eval(code: string, opts?: EvalOpts, warn?: (message: string) => void): Promise<string>;
/**
* Write some code for immediate execution. This is a lower-level
* alternative to {@link writeScript}. Be sure to include a new line character '\n' at the end.
* @param m Code
*/
write(m: string): void;
/**
* Writes a script to Espruino.
*
* It will first send a CTRL+C to cancel any previous input, `reset()` to clear the board,
* and then the provided `code` followed by a new line.
*
* Use {@link eval} instead to execute remote code and get the result back.
*
* ```js
* // Eg from https://www.espruino.com/Web+Bluetooth
* espruino.writeScript(`
* setInterval(() => Bluetooth.println(E.getTemperature()), 1000);
* NRF.on('disconnect',()=>reset());
* `);
* ```
*
* @param code Code to send. A new line is added automatically.
*/
writeScript(code: string): void;
/**
* Disconnect
*/
disconnect(): void;
/**
* Gets the current evaluation (millis)
*/
get evalTimeoutMs(): number;
get isConnected(): boolean;
} & ISimpleEventEmitter<Events>;
/**
* Evaluates some code on an Espruino device.
*
* Options:
* * timeoutMs: how many millis to wait before assuming code failed. If not specified, `device.evalTimeoutMs` is used as a default.
* * assumeExlusive: assume device is not producing any other output than for our evaluation
*
* A random string is created to pair eval requests and responses. `code` will be run on the device, with the result
* wrapped in JSON, and in turn wrapped in a object that is sent back.
*
* The actual code that gets sent to the device is then:
* `\x10${evalReplyPrefix}(JSON.stringify({reply:"${id}", result:JSON.stringify(${code})}))\n`
*
* For example, it might end up being:
* `\x10Bluetooth.println(JSON.stringify({reply: "a35gP", result: "{ 'x': '10' }" }))\n`
*
* @param code Code to evaluation
* @param opts Options for evaluation
* @param device Device to execute on
* @param evalReplyPrefix How to send code back (eg `Bluetooth.println`, `console.log`)
* @param debug If true, the full evaled code is printed locally to the console
* @param warn Callback to display warnings
* @returns
*/
declare const deviceEval: (code: string, opts: EvalOpts | undefined, device: EspruinoDevice, evalReplyPrefix: string, debug: boolean, warn: (m: string) => void) => Promise<string>;
declare namespace camera_d_exports {
export { Constraints, StartResult$1 as StartResult, dumpDevices, start$1 as start };
}
/**
* Print available media devices to console
*
* ```js
* camera.dumpDevices(); // Will print results to console
* ```
* @param filterKind Defaults `videoinput`
*/
declare const dumpDevices: (filterKind?: string) => Promise<void>;
/**
* Constraints when requesting a camera source
*/
type Constraints = {
/**
* Camera facing: user is front-facing, environment is a rear camera
*/
readonly facingMode?: `user` | `environment`;
/**
* Maximum resolution
*/
readonly max?: Rect;
/**
* Minimum resolution
*/
readonly min?: Rect;
/**
* Ideal resolution
*/
readonly ideal?: Rect;
/**
* If specified, will try to use this media device id
*/
readonly deviceId?: string;
/**
* Number of milliseconds to wait on `getUserMedia` before giving up.
* Defaults to 30seconds
*/
readonly startTimeoutMs?: number;
};
/**
* Result from starting a camera
*/
type StartResult$1 = {
/**
* Call dispose to stop the camera feed and remove any created resources,
* such as a VIDEO element
*/
readonly dispose: () => void;
/**
* Video element camera is connected to
*/
readonly videoEl: HTMLVideoElement;
};
/**
* Attempts to start a video-only stream from a camera into a hidden
* VIDEO element for frame capture. The VIDEO element is created automatically.
*
*
* ```js
* import { Camera } from '@ixfx/io.js'
* import { Video } from '@ixfx/visual.js'
* try {
* const { videoEl, dispose } = await Camera.start();
* for await (const frame of Video.frames(videoEl)) {
* // Do something with pixels...
* }
* } catch (ex) {
* console.error(`Video could not be started`);
* }
* ```
*
* Be sure to call the dispose() function to stop the video stream and remov
* the created VIDEO element.
*
* _Constraints_ can be specified to select a camera and resolution:
* ```js
* import { Camera } from '@ixfx/io.js'
* import { Video } from '@ixfx/visual.js'
*
* try {
* const { videoEl, dispose } = await Camera.start({
* facingMode: `environment`,
* max: { width: 640, height: 480 }
* });
*
* for await (const frame of Video.frames(videoEl)) {
* // Do something with pixels...
* }
* } catch (ex) {
* // Can happen if user cancels camera request, for example.
* console.error(`Video could not be started`, ex);
* }
* ```
*
* An alternative to Video.frames is Video.capture.
* @param constraints
* @returns Returns `{ videoEl, dispose }`, where `videoEl` is the created VIDEO element, and `dispose` is a function for removing the element and stopping the video.
*/
declare const start$1: (constraints?: Constraints) => Promise<StartResult$1>;
declare namespace video_file_d_exports {
export { StartResult, start };
}
/**
* Result from starting a camera
*/
type StartResult = {
/**
* Call dispose to stop the camera feed and remove any created resources,
* such as a VIDEO element
*/
readonly dispose: () => void;
/**
* Video element camera is connected to
*/
readonly videoEl: HTMLVideoElement;
};
/**
* Starts video file playback, creating a VIDEO element automatically.
* @param file File
* @returns StartResult
*/
declare const start: (file: File) => Promise<StartResult>;
//#endregion
//#region ../packages/io/src/frame-processor.d.ts
/**
* Frame procesor options
*/
type FrameProcessorOpts = {
/**
* If true, capture canvas will be shown. Default: false
*/
readonly showCanvas?: boolean;
/**
* If true, raw source will be shown. Default: false.
*/
readonly showPreview?: boolean;
/**
* If specified, this function will be called after ImageData is captured
* from the intermediate canvs. This allows for drawing on top of the
* captured image.
*/
readonly postCaptureDraw?: (ctx: CanvasRenderingContext2D, width: number, height: number) => void;
/**
* Default constraints to use for the camera source
*/
readonly cameraConstraints?: Constraints;
/**
* If specified, this canvas will be used for capturing frames to
*/
readonly captureCanvasEl?: HTMLCanvasElement;
};
/**
* Frame Processor
* Simplifies grabbing frames from a camera or video file.
*
* First, create:
* ```js
* const fp = new FrameProcessor();
* ```
*
* Then either use the camera or a video file:
* ```js
* fp.useCamera(constraints);
* // or:
* gp.useVideo(file);
* ```
*
* With `useCamera`, optionally specify {@link Camera.Constraints} to pick which camera