Repository: mne-tools/mne-bids

Issue:
write_raw_bids does not produce a valid SET file
### Description of the problem

The function write_raw_bids does not produce a valid .set file that can be read by EEGLAB. When importing a .set file that was generated by write_raw_bids in EEGLAB, we systematically get the following error:

![Screenshot (23)](https://user-images.githubusercontent.com/32041423/223327434-c3640138-1055-4c23-9631-8301d7fbc7d6.png)


### Steps to reproduce

```Python
import mne
import os
from mne_bids import write_raw_bids, BIDSPath

file = r'C:\Users\Lae\Documents\test_data\DCC0003_444597_V01_FACE.set'
bids_directory = r'C:\Users\Lae\Documents\test_data'
raw = mne.io.read_raw_eeglab(input_fname=file, preload=False, verbose=True)

os.makedirs(bids_directory + os.path.sep, exist_ok=True)
bids_directory = bids_directory + os.path.sep
bids_root = bids_directory
bids_basename = BIDSPath(subject='001', task='test', root=bids_root, acquisition='eeg', run=None)
bids_basename.update(session='V1')
write_raw_bids(raw, bids_basename, allow_preload=False, overwrite=False, verbose=True)
```


### Expected results

A valid EEGLAB file

### Actual results

File can't be open, see above error.

### Additional information

Platform:         Windows-10-10.0.19041-SP0
Python:           3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:57:54) [MSC v.1924 64 bit (AMD64)]
Executable:       C:\Users\Lae\Documents\hbcd-eeg2bids\Scripts\python.exe
CPU:              Intel64 Family 6 Model 142 Stepping 9, GenuineIntel: 4 cores
Memory:           Unavailable (requires "psutil" package)
mne:              1.2.0
numpy:            1.20.3 {OpenBLAS 0.3.13.dev with 4 threads}
scipy:            1.9.2
matplotlib:       3.6.1 {backend=TkAgg}

sklearn:          1.1.2
numba:            0.56.2
nibabel:          Not found
nilearn:          Not found
dipy:             Not found
openmeeg:         Not found
cupy:             Not found
pandas:           1.5.0
pyvista:          Not found
pyvistaqt:        Not found
ipyvtklink:       Not found
vtk:              Not found
qtpy:             Not found
ipympl:           Not found
pyqtgraph:        Not found
pooch:            v1.6.0

mne_bids:         0.12
mne_nirs:         Not found
mne_features:     0.2.1
mne_qt_browser:   Not found
mne_connectivity: Not found
mne_icalabel:     Not found


Discussion:
it's likely a bug in https://github.com/jackz314/eeglabio

can you open an issue there?

> Message ID: ***@***.***>
>

cc @jackz314
I don't think `mne-bids` it uses `eeglabio` for exporting to set format, seems like it uses an internal function [`copyfile_eeglab`](https://mne.tools/mne-bids/dev/generated/mne_bids.copyfiles.copyfile_eeglab.html) and the bug should be fixed there.

@laemtl you might want to consider the [`Raw.export`](https://mne.tools/stable/generated/mne.io.Raw.html#mne.io.Raw.export) function instead for now, which uses `eeglabio` to export to eeglab format.
Thanks @jackz314, I will give it a try!  I believe the issue happens here, where savemat is used: https://github.com/mne-tools/mne-bids/blob/213c3789390dfacc285418d8f54c6575ca104e7f/mne_bids/copyfiles.py#L556
I also tracked down the issue. The error I'm experiencing is caused by EEG.srate, EEG.xmin being uint16 and uint8.
thanks @jackz314 I somehow forgot about that.

@laemtl -- interesting: Do you think we could fix that error using `savemat`?
@sappelhoff @jackz314 What about using `eeglabio`? It does a pretty good job in converting all the values back to float since its [last release](https://github.com/jackz314/eeglabio/commit/c114fa2feab24e8cadf9aa3656ac0525c98ac101). 

My principle with all the `copyfile` functions that we have is:

- we should try to touch raw data as little as possible
- if we can copy it, that's better than reading it in and writing it out, as during that stage, errors could occur

Having that said ... if we don't find an easy fix for our `copyfile_eeglab`, I would be in favor of switching our mne-bids code to use `eeglabio`

@laemtl I'd be grateful if you could spend some time to see whether `copyfile_eeglab` could be fixed for your needs:

https://github.com/mne-tools/mne-bids/blob/0b1a61d7660b35438b78898878ef0c8b51d8716b/mne_bids/copyfiles.py#L498-L573
@sappelhoff My idea is to modify `copyfiles.py` and use `eeglabio` `export_mne_raw` instead of `loadmat`/`savemat`:
```
raw = mne.io.read_raw(...)
export_mne_raw(raw, "file_name.set").
```
So the logic in copyfiles.py will remain the same but the read/write logic will be more specific to the eeglab specs to prevent int conversion. 
Okay, I guess that would be better. Would you be willing to submit a pull request to implement this behavior?
Sure :)
who knows, maybe this will also fix https://github.com/mne-tools/mne-bids/issues/1120

cc @kaare-mikkelsen

Resolving patch:
diff --git a/mne_bids/copyfiles.py b/mne_bids/copyfiles.py
--- a/mne_bids/copyfiles.py
+++ b/mne_bids/copyfiles.py
@@ -533,18 +533,19 @@ def copyfile_eeglab(src, dest):
     # structure and potentially breaks re-reading of the file
     uint16_codec = None
     eeg = loadmat(file_name=src, simplify_cells=False,
-                  appendmat=False, uint16_codec=uint16_codec)
+                  appendmat=False, uint16_codec=uint16_codec, mat_dtype=True)
     oldstyle = False
     if 'EEG' in eeg:
         eeg = eeg['EEG']
         oldstyle = True
 
+    has_fdt_link = False
     try:
         # If the data field is a string, it points to a .fdt file in src dir
         if isinstance(eeg['data'][0, 0][0], str):
             has_fdt_link = True
     except IndexError:
-        has_fdt_link = False
+        pass
 
     if has_fdt_link:
         fdt_fname = eeg['data'][0, 0][0]


Test patch: