convertCytonSdToXdf function

Future<CytonSdSummary> convertCytonSdToXdf(
  1. Stream<List<int>> input,
  2. Sink<List<int>> sink, {
  3. String name = 'OpenBCI Cyton',
  4. List<String>? labels,
  5. List<String>? types,
  6. double? rate,
  7. List<int>? gains,
  8. CytonBoardMode? mode,
  9. int layoutLines = 2500,
})

Convert an SD card recording, read from input in pieces, to XDF on sink, without holding the recording: a stream of its channels (µV) named name, with labels and types (e.g. EEG, EMG) per channel, one of its accelerometer (g) if it has one, and one of its markers if it has any. Time stamps start at 0. Reading stops at the file's unused space.

rate, gains and mode are the board's settings, where known; the first layoutLines lines decide the rest (see CytonSdLayout.of).

Implementation

Future<CytonSdSummary> convertCytonSdToXdf(
  Stream<List<int>> input,
  Sink<List<int>> sink, {
  String name = 'OpenBCI Cyton',
  List<String>? labels,
  List<String>? types,
  double? rate,
  List<int>? gains,
  CytonBoardMode? mode,
  int layoutLines = 2500,
}) async {
  final reader = CytonSdReader(gains: gains);
  final pending = <CytonSdRow>[];
  _Writer? out;
  _Writer start() => _Writer(
    sink,
    CytonSdLayout.of(pending, rate: rate, mode: mode),
    reader.channelCount ?? 8,
    name,
    labels,
    types,
  );
  await for (final bytes in input) {
    pending.addAll(reader.add(bytes));
    if (out == null && pending.length >= layoutLines) out = start();
    if (out != null) {
      out.write(pending);
      pending.clear();
    }
    if (reader.done) break;
  }
  pending.addAll(reader.close());
  out ??= start();
  out.write(pending);
  await out.writer.close();
  return CytonSdSummary(
    out.channels,
    out.layout,
    out.samples,
    out.markers,
    reader.lost,
    reader.skipped,
    reader.metadata,
  );
}