diff --git a/cmlreaders/cmlreader.py b/cmlreaders/cmlreader.py
index ba4dc1e..f1d6e4c 100644
--- a/cmlreaders/cmlreader.py
+++ b/cmlreaders/cmlreader.py
@@ -218,7 +218,7 @@ class CMLReader(object):
 
             - (start_index, stop_index)
             - (start_index, stop_index, file_number) when the EEG
-            for the session is is split over multiple recordings.
+              for the session is is split over multiple recordings.
 
             Incompatible with passing ``events``.
         scheme
diff --git a/cmlreaders/convert.py b/cmlreaders/convert.py
new file mode 100644
index 0000000..2f12839
--- /dev/null
+++ b/cmlreaders/convert.py
@@ -0,0 +1,100 @@
+from typing import List, Optional, Tuple, Union
+import pandas as pd
+
+
+def milliseconds_to_samples(millis: Union[int, float],
+                            sample_rate: Union[int, float]) -> int:
+    """Covert times in milliseconds to number of samples.
+
+    Parameters
+    ----------
+    millis
+        Time in ms.
+    sample_rate
+        Sample rate in samples per second.
+
+    Returns
+    -------
+    Number of samples.
+
+    """
+    return int(sample_rate * millis / 1000.)
+
+
+def samples_to_milliseconds(samples: int,
+                            sample_rate: Union[int, float]) -> Union[int, float]:
+    """Convert samples to milliseconds.
+
+    Parameters
+    ----------
+    samples
+        Number of samples.
+    sample_rate
+        Sample rate in samples per second.
+
+    Returns
+    -------
+    Samples converted to milliseconds.
+
+    """
+    return 1000 * samples / sample_rate
+
+
+def milliseconds_to_events(onsets: List[Union[int, float]],
+                           sample_rate: Union[int, float]) -> pd.DataFrame:
+    """Take times and produce a minimal events :class:`pd.DataFrame` to load
+    EEG data with.
+
+    Parameters
+    ----------
+    onsets
+        Onset times in ms.
+    sample_rate
+        Sample rate in samples per second.
+
+    Returns
+    -------
+    events
+        A :class:`pd.DataFrame` with ``eegoffset`` as the only column.
+
+    """
+    samples = [milliseconds_to_samples(onset, sample_rate) for onset in onsets]
+    return pd.DataFrame({"eegoffset": samples})
+
+
+def events_to_epochs(events: pd.DataFrame, rel_start: int, rel_stop: int,
+                     sample_rate: Union[int, float],
+                     basenames: Optional[List[str]] = None
+                     ) -> List[Tuple[int, int, int]]:
+    """Convert events to epochs.
+
+    Parameters
+    ----------
+    events
+        Events to read.
+    rel_start
+        Start time relative to events in ms.
+    rel_stop
+        Stop time relative to events in ms.
+    sample_rate
+        Sample rate in Hz.
+
+    Returns
+    -------
+    epochs
+        A list of tuples giving absolute start and stop times in number of
+        samples.
+
+    """
+    rel_start = milliseconds_to_samples(rel_start, sample_rate)
+    rel_stop = milliseconds_to_samples(rel_stop, sample_rate)
+    offsets = events.eegoffset.values
+    if basenames is not None:
+        eegfiles = events.eegfile.values
+        epochs = [(offset + rel_start, offset + rel_stop,
+                   basenames.index(eegfile))
+                  for (offset, eegfile) in zip(offsets, eegfiles)]
+    else:
+        epochs = [(offset + rel_start, offset + rel_stop, 0)
+                  for offset in offsets]
+    return epochs
diff --git a/cmlreaders/readers/__init__.py b/cmlreaders/readers/__init__.py
index 208557d..618f123 100644
--- a/cmlreaders/readers/__init__.py
+++ b/cmlreaders/readers/__init__.py
@@ -1,19 +1,22 @@
-# flake8: noqa
+def _import_readers():
+    import importlib
+    import pkgutil
 
-# TODO: find a way to automatically import all readers
+    loader = pkgutil.get_loader("cmlreaders")
+    module_info = [info for info in pkgutil.walk_packages(loader.path)
+                   if info[1].startswith("cmlreaders.readers.")]
+    modules = [
+        importlib.import_module("." + info[1].split(".")[-1],
+                                package="cmlreaders.readers")
+        for info in module_info
+    ]
 
-from .eeg import EEGReader
+    for module in modules:
+        things = [getattr(module, thing) for thing in dir(module)
+                  if thing.endswith("Reader")]
 
-from .electrodes import (
-    ElectrodeCategoriesReader,
-    LocalizationReader,
-    MontageReader,
-)
+        for thing in things:
+            globals()[thing.__name__] = thing
 
-from .readers import *
 
-from .reports import (
-    BaseRAMReportDataReader,
-    RAMReportClassifierSummaryReader,
-    RAMReportSummaryDataReader,
-)
+_import_readers()
diff --git a/cmlreaders/readers/eeg.py b/cmlreaders/readers/eeg.py
index 537a6aa..709e3a5 100644
--- a/cmlreaders/readers/eeg.py
+++ b/cmlreaders/readers/eeg.py
@@ -15,6 +15,7 @@ with warnings.catch_warnings():  # noqa
 import numpy as np
 import pandas as pd
 
+from cmlreaders import convert
 from cmlreaders.base_reader import BaseCMLReader
 from cmlreaders.exc import (
     RereferencingNotPossibleError, UnsupportedOutputFormat
@@ -24,111 +25,6 @@ from cmlreaders.timeseries import TimeSeries
 from cmlreaders.util import DefaultTuple
 
 
-__all__ = [
-    "EEGReader",
-    "milliseconds_to_events",
-    "milliseconds_to_samples",
-]
-
-
-def milliseconds_to_samples(millis: Union[int, float],
-                            sample_rate: Union[int, float]) -> int:
-    """Covert times in milliseconds to number of samples.
-
-    Parameters
-    ----------
-    millis
-        Time in ms.
-    sample_rate
-        Sample rate in samples per second.
-
-    Returns
-    -------
-    Number of samples.
-
-    """
-    return int(sample_rate * millis / 1000.)
-
-
-def samples_to_milliseconds(samples: int,
-                            sample_rate: Union[int, float]) -> Union[int, float]:
-    """Convert samples to milliseconds.
-
-    Parameters
-    ----------
-    samples
-        Number of samples.
-    sample_rate
-        Sample rate in samples per second.
-
-    Returns
-    -------
-    Samples converted to milliseconds.
-
-    """
-    return 1000 * samples / sample_rate
-
-
-def milliseconds_to_events(onsets: List[Union[int, float]],
-                           sample_rate: Union[int, float]) -> pd.DataFrame:
-    """Take times and produce a minimal events :class:`pd.DataFrame` to load
-    EEG data with.
-
-    Parameters
-    ----------
-    onsets
-        Onset times in ms.
-    sample_rate
-        Sample rate in samples per second.
-
-    Returns
-    -------
-    events
-        A :class:`pd.DataFrame` with ``eegoffset`` as the only column.
-
-    """
-    samples = [milliseconds_to_samples(onset, sample_rate) for onset in onsets]
-    return pd.DataFrame({"eegoffset": samples})
-
-
-def events_to_epochs(events: pd.DataFrame, rel_start: int, rel_stop: int,
-                     sample_rate: Union[int, float],
-                     basenames: Optional[List[str]] = None
-                     ) -> List[Tuple[int, int, int]]:
-    """Convert events to epochs.
-
-    Parameters
-    ----------
-    events
-        Events to read.
-    rel_start
-        Start time relative to events in ms.
-    rel_stop
-        Stop time relative to events in ms.
-    sample_rate
-        Sample rate in Hz.
-
-    Returns
-    -------
-    epochs
-        A list of tuples giving absolute start and stop times in number of
-        samples.
-
-    """
-    rel_start = milliseconds_to_samples(rel_start, sample_rate)
-    rel_stop = milliseconds_to_samples(rel_stop, sample_rate)
-    offsets = events.eegoffset.values
-    if basenames is not None:
-        eegfiles = events.eegfile.values
-        epochs = [(offset + rel_start, offset + rel_stop,
-                   basenames.index(eegfile))
-                  for (offset, eegfile) in zip(offsets, eegfiles)]
-    else:
-        epochs = [(offset + rel_start, offset + rel_stop, 0)
-                  for offset in offsets]
-    return epochs
-
-
 class BaseEEGReader(ABC):
     """Base class for actually reading EEG data. Subclasses will be used by
     :class:`EEGReader` to actually read the format-specific EEG data.
@@ -401,11 +297,11 @@ class EEGReader(BaseCMLReader):
             source_info_list = list(self.sources_info.values())[:-1]
             sample_rate = source_info_list[0]['sample_rate']
             basenames = [info['name'] for info in source_info_list]
-            epochs = events_to_epochs(events,
-                                      kwargs.pop('rel_start'),
-                                      kwargs.pop('rel_stop'),
-                                      sample_rate,
-                                      basenames)
+            epochs = convert.events_to_epochs(events,
+                                              kwargs.pop('rel_start'),
+                                              kwargs.pop('rel_stop'),
+                                              sample_rate,
+                                              basenames)
             kwargs['epochs'] = epochs
 
         if "epochs" not in kwargs:
diff --git a/cmlreaders/readers/readers.py b/cmlreaders/readers/readers.py
index c4783aa..d492644 100644
--- a/cmlreaders/readers/readers.py
+++ b/cmlreaders/readers/readers.py
@@ -8,11 +8,6 @@ from cmlreaders.exc import (
 )
 
 
-__all__ = ['TextReader', 'RAMCSVReader', 'RamulatorEventLogReader',
-           'BaseJSONReader', 'EventReader',
-           'ClassifierContainerReader', 'EEGMetaReader']
-
-
 # TODO: separate into a base class so that we can use this for ltp
 class TextReader(BaseCMLReader):
     """ Generic reader class for reading RAM text files """
diff --git a/docs/api.rst b/docs/api.rst
index 80a7f1e..ac35117 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -10,6 +10,12 @@ CML Reader
 .. autoclass:: cmlreaders.CMLReader
     :members:
 
+Unit conversions
+================
+
+.. automodule:: cmlreaders.convert
+    :members:
+
 Custom Readers
 ==============
 
@@ -40,6 +46,3 @@ Path and File Constants
 :class:`cmlreaders.PathFinder` internally uses the :mod:`cmlreaders.constants`
 module. The usefulness of :class:`cmlreaders.PathFinder` relies on these
 constants being well-maintained.
-
-
-
diff --git a/docs/dev.rst b/docs/dev.rst
index 7bc4843..24d2868 100644
--- a/docs/dev.rst
+++ b/docs/dev.rst
@@ -56,6 +56,16 @@ to the ``data_types`` list.
         def as_dict(self):
             return {'for': 'great justice'}
 
+By default, all known protocols (e.g., ``r1``, ``ltp``) are assumed to be
+supported. If a reader only works for a subset, specify the ``protocols`` class
+variable:
+
+.. code-block:: python
+
+    class RamThingReader(BaseCMLReader):
+        data_types = ["ram_thing"]
+        protocols = ["r1"]
+
 Once the reader works, test cases for the data types using the new reader
 should be added to :mod:`cmlreaders.test.test_cmlreader`. These are in addition
 to the test cases that should already exist for the new reader. For examples,

Simplify reader setup
As things stand, we have to import any new readers in `cmlreaders/readers/__init__.py`. This should be reworked to either automatically discover readers to import or restructure things to make this unnecessary.
diff --git a/cmlreaders/test/test_convert.py b/cmlreaders/test/test_convert.py
new file mode 100644
index 0000000..c780500
--- /dev/null
+++ b/cmlreaders/test/test_convert.py
@@ -0,0 +1,75 @@
+from numpy.testing import assert_equal
+import pandas as pd
+from pkg_resources import resource_filename
+import pytest
+
+from cmlreaders.cmlreader import CMLReader
+from cmlreaders.convert import (
+    events_to_epochs,
+    milliseconds_to_events,
+    milliseconds_to_samples,
+    samples_to_milliseconds,
+)
+from cmlreaders.test.utils import patched_cmlreader
+
+
+@pytest.fixture
+def events():
+    with patched_cmlreader():
+        cml_reader = CMLReader("R1389J")
+        path = resource_filename('cmlreaders.test.data', 'all_events.json')
+        reader = cml_reader.get_reader('events', file_path=path)
+        return reader.as_dataframe()
+
+
+@pytest.mark.parametrize("millis,rate,samples", [
+    (1000, 1000, 1000),
+    (1000, 500, 500),
+    (1000, 250, 250),
+])
+def test_milliseconds_to_samples(millis, rate, samples):
+    assert milliseconds_to_samples(millis, rate) == samples
+
+
+@pytest.mark.parametrize("samples,rate,millis", [
+    (1000, 1000, 1000),
+    (1000, 500, 2000),
+    (1000, 250, 4000),
+])
+def test_samples_to_milliseconds(samples, rate, millis):
+    assert samples_to_milliseconds(samples, rate) == millis
+
+
+@pytest.mark.parametrize("onsets,rate,expected", [
+    ([0], 1000, [0]),
+    ([10], 100, [1])
+])
+def test_milliseconds_to_events(onsets, rate, expected):
+    df = milliseconds_to_events(onsets, rate)
+    assert_equal(expected, df.eegoffset.values)
+
+
+@pytest.mark.parametrize('rel_start', [-100, 0])
+@pytest.mark.parametrize('rel_stop', [100, 500])
+def test_events_to_epochs(events, rel_start, rel_stop):
+    words = events[events.type == 'WORD']
+
+    epochs = events_to_epochs(words, rel_start, rel_stop, 1000)
+    assert len(epochs) == 156
+    for epoch in epochs:
+        assert epoch[1] - epoch[0] == rel_stop - rel_start
+
+
+def test_events_to_epochs_simple():
+    offsets = [100, 200]  # in samples
+    events = pd.DataFrame({"eegoffset": offsets})
+    rate = 1000  # in samples / s
+    rel_start = -10  # in ms
+    rel_stop = 10  # in ms
+
+    epochs = events_to_epochs(events, rel_start, rel_stop, rate)
+
+    assert epochs[0][0] == 90
+    assert epochs[0][1] == 110
+    assert epochs[1][0] == 190
+    assert epochs[1][1] == 210
diff --git a/cmlreaders/test/test_eeg.py b/cmlreaders/test/test_eeg.py
index cc9c2e6..b4c347e 100644
--- a/cmlreaders/test/test_eeg.py
+++ b/cmlreaders/test/test_eeg.py
@@ -13,11 +13,10 @@ from numpy.testing import assert_equal
 import pandas as pd
 
 from cmlreaders import CMLReader, PathFinder
-from cmlreaders import exc
+from cmlreaders import convert, exc
 from cmlreaders.readers.eeg import (
-    events_to_epochs, milliseconds_to_events, milliseconds_to_samples,
     BaseEEGReader, EEGReader, NumpyEEGReader, RamulatorHDF5Reader,
-    samples_to_milliseconds, SplitEEGReader,
+    SplitEEGReader,
 )
 from cmlreaders.readers import MontageReader
 from cmlreaders.test.utils import patched_cmlreader
@@ -32,59 +31,6 @@ def events():
         return reader.as_dataframe()
 
 
-@pytest.mark.parametrize("millis,rate,samples", [
-    (1000, 1000, 1000),
-    (1000, 500, 500),
-    (1000, 250, 250),
-])
-def test_milliseconds_to_samples(millis, rate, samples):
-    assert milliseconds_to_samples(millis, rate) == samples
-
-
-@pytest.mark.parametrize("samples,rate,millis", [
-    (1000, 1000, 1000),
-    (1000, 500, 2000),
-    (1000, 250, 4000),
-])
-def test_samples_to_milliseconds(samples, rate, millis):
-    assert samples_to_milliseconds(samples, rate) == millis
-
-
-@pytest.mark.parametrize("onsets,rate,expected", [
-    ([0], 1000, [0]),
-    ([10], 100, [1])
-])
-def test_milliseconds_to_events(onsets, rate, expected):
-    df = milliseconds_to_events(onsets, rate)
-    assert_equal(expected, df.eegoffset.values)
-
-
-@pytest.mark.parametrize('rel_start', [-100, 0])
-@pytest.mark.parametrize('rel_stop', [100, 500])
-def test_events_to_epochs(events, rel_start, rel_stop):
-    words = events[events.type == 'WORD']
-
-    epochs = events_to_epochs(words, rel_start, rel_stop, 1000)
-    assert len(epochs) == 156
-    for epoch in epochs:
-        assert epoch[1] - epoch[0] == rel_stop - rel_start
-
-
-def test_events_to_epochs_simple():
-    offsets = [100, 200]  # in samples
-    events = pd.DataFrame({"eegoffset": offsets})
-    rate = 1000  # in samples / s
-    rel_start = -10  # in ms
-    rel_stop = 10  # in ms
-
-    epochs = events_to_epochs(events, rel_start, rel_stop, rate)
-
-    assert epochs[0][0] == 90
-    assert epochs[0][1] == 110
-    assert epochs[1][0] == 190
-    assert epochs[1][1] == 210
-
-
 class TestBaseEEGReader:
     @pytest.mark.parametrize("use_scheme", [True, False])
     def test_include_contact(self, use_scheme):
@@ -141,7 +87,7 @@ class TestFileReaders:
 
         events = pd.DataFrame({"eegoffset": list(range(0, 500, 100))})
         rel_start, rel_stop = 0, 200
-        epochs = events_to_epochs(events, rel_start, rel_stop, sample_rate)
+        epochs = convert.events_to_epochs(events, rel_start, rel_stop, sample_rate)
 
         eeg_reader = SplitEEGReader(filename, dtype, epochs, None)
         ts, contacts = eeg_reader.read()
@@ -155,7 +101,7 @@ class TestFileReaders:
 
         events = pd.DataFrame({"eegoffset": list(range(0, 500, 100))})
         rel_start, rel_stop = 0, 200
-        epochs = events_to_epochs(events, rel_start, rel_stop, sample_rate)
+        epochs = convert.events_to_epochs(events, rel_start, rel_stop, sample_rate)
 
         eeg_reader = SplitEEGReader(filename, dtype, epochs, None)
         ts, contacts = eeg_reader.read()
@@ -179,7 +125,7 @@ class TestFileReaders:
 
         events = pd.DataFrame({"eegoffset": list(range(0, 500, 100))})
         rel_start, rel_stop = 0, 200
-        epochs = events_to_epochs(events, rel_start, rel_stop, sample_rate)
+        epochs = convert.events_to_epochs(events, rel_start, rel_stop, sample_rate)
 
         eeg_reader = RamulatorHDF5Reader(filename, dtype, epochs, None)
         ts, contacts = eeg_reader.read()
@@ -281,7 +227,7 @@ class TestEEGReader:
 
         events = pd.DataFrame({"eegoffset": [0]})
         rel_start, rel_stop = 0, 100
-        epochs = events_to_epochs(events, rel_start, rel_stop, rate)
+        epochs = convert.events_to_epochs(events, rel_start, rel_stop, rate)
 
         expected_samples = int(rate * rel_stop / 1000)
         scheme = reader.load('pairs')

pip install -e .
parse_log_pytest
requirements.txt
pytest pytest-cov pytest-xdist
3.6
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.13
async-generator==1.10
attrs==22.2.0
Babel==2.11.0
bleach==4.1.0
cached-property==1.5.2
certifi==2021.5.30
charset-normalizer==2.0.12
-e git+https://github.com/pennmem/cmlreaders.git@9e6bb8b35d4db331738ca1b2f877e427282e1531#egg=cmlreaders
codecov==2.1.13
coverage==6.2
decorator==5.1.1
defusedxml==0.7.1
docutils==0.18.1
entrypoints==0.4
execnet==1.9.0
flake8==3.9.2
h5py==3.1.0
idna==3.10
imagesize==1.4.1
importlib-metadata==4.8.3
iniconfig==1.1.1
ipython-genutils==0.2.0
Jinja2==3.0.3
jsonschema==3.2.0
jupyter-client==7.1.2
jupyter-core==4.9.2
jupyterlab-pygments==0.1.2
MarkupSafe==2.0.1
mccabe==0.6.1
mistune==0.8.4
mne==0.23.4
nbclient==0.5.9
nbconvert==6.0.7
nbformat==5.1.3
nbsphinx==0.8.8
nest-asyncio==1.6.0
numpy==1.19.5
packaging==21.3
pandas==1.1.5
pandocfilters==1.5.1
pluggy==1.0.0
py==1.11.0
pycodestyle==2.7.0
pyflakes==2.3.1
Pygments==2.14.0
pyparsing==3.1.4
pyrsistent==0.18.0
pytest==7.0.1
pytest-cov==4.0.0
pytest-xdist==3.0.2
python-dateutil==2.9.0.post0
pytz==2025.2
pyzmq==25.1.2
requests==2.27.1
scipy==1.5.4
six==1.17.0
snowballstemmer==2.2.0
Sphinx==5.3.0
sphinx-rtd-theme==2.0.0
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.0
sphinxcontrib-jquery==4.1
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
testpath==0.6.0
tomli==1.2.3
tornado==6.1
traitlets==4.3.3
typing_extensions==4.1.1
urllib3==1.26.20
webencodings==0.5.1
zipp==3.6.0

name: cmlreaders
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alabaster==0.7.13
      - async-generator==1.10
      - attrs==22.2.0
      - babel==2.11.0
      - bleach==4.1.0
      - cached-property==1.5.2
      - charset-normalizer==2.0.12
      - codecov==2.1.13
      - coverage==6.2
      - decorator==5.1.1
      - defusedxml==0.7.1
      - docutils==0.18.1
      - entrypoints==0.4
      - execnet==1.9.0
      - flake8==3.9.2
      - h5py==3.1.0
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - ipython-genutils==0.2.0
      - jinja2==3.0.3
      - jsonschema==3.2.0
      - jupyter-client==7.1.2
      - jupyter-core==4.9.2
      - jupyterlab-pygments==0.1.2
      - markupsafe==2.0.1
      - mccabe==0.6.1
      - mistune==0.8.4
      - mne==0.23.4
      - nbclient==0.5.9
      - nbconvert==6.0.7
      - nbformat==5.1.3
      - nbsphinx==0.8.8
      - nest-asyncio==1.6.0
      - numpy==1.19.5
      - packaging==21.3
      - pandas==1.1.5
      - pandocfilters==1.5.1
      - pluggy==1.0.0
      - py==1.11.0
      - pycodestyle==2.7.0
      - pyflakes==2.3.1
      - pygments==2.14.0
      - pyparsing==3.1.4
      - pyrsistent==0.18.0
      - pytest==7.0.1
      - pytest-cov==4.0.0
      - pytest-xdist==3.0.2
      - python-dateutil==2.9.0.post0
      - pytz==2025.2
      - pyzmq==25.1.2
      - requests==2.27.1
      - scipy==1.5.4
      - six==1.17.0
      - snowballstemmer==2.2.0
      - sphinx==5.3.0
      - sphinx-rtd-theme==2.0.0
      - sphinxcontrib-applehelp==1.0.2
      - sphinxcontrib-devhelp==1.0.2
      - sphinxcontrib-htmlhelp==2.0.0
      - sphinxcontrib-jquery==4.1
      - sphinxcontrib-jsmath==1.0.1
      - sphinxcontrib-qthelp==1.0.3
      - sphinxcontrib-serializinghtml==1.1.5
      - testpath==0.6.0
      - tomli==1.2.3
      - tornado==6.1
      - traitlets==4.3.3
      - typing-extensions==4.1.1
      - urllib3==1.26.20
      - webencodings==0.5.1
      - zipp==3.6.0
prefix: /opt/conda/envs/cmlreaders

cmlreaders/test/test_convert.py::test_milliseconds_to_samples[1000-1000-1000] cmlreaders/test/test_convert.py::test_milliseconds_to_samples[1000-500-500] cmlreaders/test/test_convert.py::test_milliseconds_to_samples[1000-250-250] cmlreaders/test/test_convert.py::test_samples_to_milliseconds[1000-1000-1000] cmlreaders/test/test_convert.py::test_samples_to_milliseconds[1000-500-2000] cmlreaders/test/test_convert.py::test_samples_to_milliseconds[1000-250-4000] cmlreaders/test/test_convert.py::test_milliseconds_to_events[onsets0-1000-expected0] cmlreaders/test/test_convert.py::test_milliseconds_to_events[onsets1-100-expected1] cmlreaders/test/test_convert.py::test_events_to_epochs[100--100] cmlreaders/test/test_convert.py::test_events_to_epochs[100-0] cmlreaders/test/test_convert.py::test_events_to_epochs[500--100] cmlreaders/test/test_convert.py::test_events_to_epochs[500-0] cmlreaders/test/test_convert.py::test_events_to_epochs_simple cmlreaders/test/test_eeg.py::TestBaseEEGReader::test_include_contact[True] cmlreaders/test/test_eeg.py::TestBaseEEGReader::test_include_contact[False] cmlreaders/test/test_eeg.py::TestFileReaders::test_npy_reader cmlreaders/test/test_eeg.py::TestFileReaders::test_ramulator_hdf5_reader cmlreaders/test/test_eeg.py::TestFileReaders::test_ramulator_hdf5_rereference cmlreaders/test/test_eeg.py::TestRereference::test_rereference[SplitEEGReader-True]
cmlreaders/test/test_eeg.py::TestFileReaders::test_split_eeg_reader cmlreaders/test/test_eeg.py::TestFileReaders::test_split_eeg_reader_missing_contacts cmlreaders/test/test_eeg.py::TestFileReaders::test_ramulator_hdf5_reader_rhino[R1363T-FR1-0-178-True] cmlreaders/test/test_eeg.py::TestFileReaders::test_ramulator_hdf5_reader_rhino[R1392N-PAL1-0-112-False] cmlreaders/test/test_eeg.py::TestEEGReader::test_eeg_reader[R1298E-87-CH88] cmlreaders/test/test_eeg.py::TestEEGReader::test_eeg_reader[R1387E-13-CH14] cmlreaders/test/test_eeg.py::TestEEGReader::test_read_whole_session[R1161E] cmlreaders/test/test_eeg.py::TestEEGReader::test_eeg_reader_with_events[R1161E] cmlreaders/test/test_eeg.py::TestEEGReader::test_eeg_reader_with_events[R1387E] cmlreaders/test/test_eeg.py::TestEEGReader::test_rereference[R1384J-False-43-LS12-LS1] cmlreaders/test/test_eeg.py::TestEEGReader::test_rereference[R1111M-True-43-LPOG23-LPOG31] cmlreaders/test/test_eeg.py::TestEEGReader::test_filter_channels[R1384J-ind.region-insula-10-200] cmlreaders/test/test_eeg.py::TestEEGReader::test_filter_channels[R1288P-ind.region-lateralorbitofrontal-5-200] cmlreaders/test/test_eeg.py::TestEEGReader::test_filter_channels[R1111M-ind.region-middletemporal-18-100] cmlreaders/test/test_eeg.py::TestRereference::test_rereference[RamulatorHDF5Reader-True] cmlreaders/test/test_eeg.py::TestRereference::test_rereference[RamulatorHDF5Reader-False]
cmlreaders/cmlreader.py docs/dev.rst cmlreaders/readers/eeg.py cmlreaders/readers/readers.py docs/api.rst cmlreaders/readers/__init__.py cmlreaders/convert.py
cmlreaders/cmlreader.py docs/dev.rst cmlreaders/readers/eeg.py cmlreaders/readers/readers.py docs/api.rst cmlreaders/readers/__init__.py cmlreaders/convert.py