Repository: mne-tools/mne-python

Issue:
Brainvision stim channel creation fails if event durations are 0
Stim channels for brainvision and EEGLAB files are created like this:

```
    for onset, duration, trigger in events:
        stim_channel[onset:onset + duration] = trigger
```

If the duration is 0, which it often is, this will result in no events being recorded.

I am not sure how to deal with this.

The events are still available to the user at `raw._events`, but `mne.find_events` etc won't work.


Discussion:
we should probably make the min duration 1 sample
BV vmrk files have a duration column that is set to 0 for the file I'm looking at ... but yeah, we can, i.e., change

```
    for onset, duration, trigger in events:
        stim_channel[onset:onset + duration] = trigger
```

to

```
    for onset, duration, trigger in events:
        stim_channel[onset:onset + (duration if duration > 0 else 1)] = trigger
```

and add a test.

(Thinking of Cornelius here :))
I've just had somebody contact me with this problem (as it turned out) - I'm definitely +1 on Jona's proposal.
agreed

Resolving patch:
diff --git a/mne/io/brainvision/brainvision.py b/mne/io/brainvision/brainvision.py
--- a/mne/io/brainvision/brainvision.py
+++ b/mne/io/brainvision/brainvision.py
@@ -287,6 +287,9 @@ def _read_vmrk_events(fname, event_id=None, response_trig_shift=0):
                     trigger += response_trig_shift
                 else:
                     trigger = None
+        # FIXME: ideally, we would not use the middle column of the events
+        # array to store the duration. A better solution would be using
+        # annotations.
         if trigger:
             events.append((onset, duration, trigger))
         else:
diff --git a/mne/io/utils.py b/mne/io/utils.py
--- a/mne/io/utils.py
+++ b/mne/io/utils.py
@@ -231,6 +231,7 @@ def _synthesize_stim_channel(events, n_samples):
     events : array, shape (n_events, 3)
         Each row representing an event as (onset, duration, trigger) sequence
         (the format returned by `_read_vmrk_events` or `_read_eeglab_events`).
+        Durations equal to 0 are converted to 1.
     n_samples : int
         The number of samples.
 
@@ -241,6 +242,8 @@ def _synthesize_stim_channel(events, n_samples):
     """
     # select events overlapping buffer
     onset = events[:, 0]
+    events = events.copy()
+    events[events[:, 1] < 1, 1] = 1
     # create output buffer
     stim_channel = np.zeros(n_samples, int)
     for onset, duration, trigger in events:


Test patch:
diff --git a/mne/io/brainvision/tests/data/test.vmrk b/mne/io/brainvision/tests/data/test.vmrk
--- a/mne/io/brainvision/tests/data/test.vmrk
+++ b/mne/io/brainvision/tests/data/test.vmrk
@@ -10,7 +10,7 @@ DataFile=test.eeg
 ; Fields are delimited by commas, some fields might be omitted (empty).
 ; Commas in type or description text are coded as "\1".
 Mk1=New Segment,,1,1,0,20131113161403794232
-Mk2=Stimulus,S253,487,1,0
+Mk2=Stimulus,S253,487,0,0
 Mk3=Stimulus,S255,497,1,0
 Mk4=Stimulus,S254,1770,1,0
 Mk5=Stimulus,S255,1780,1,0
diff --git a/mne/io/brainvision/tests/test_brainvision.py b/mne/io/brainvision/tests/test_brainvision.py
--- a/mne/io/brainvision/tests/test_brainvision.py
+++ b/mne/io/brainvision/tests/test_brainvision.py
@@ -376,7 +376,7 @@ def test_events():
     raw = read_raw_brainvision(vhdr_path, eog=eog, event_id=event_id)
     events = raw._get_brainvision_events()
     events = events[events[:, 2] != event_id['Sync On']]
-    assert_array_equal(events, [[487, 1, 253],
+    assert_array_equal(events, [[487, 0, 253],
                                 [497, 1, 255],
                                 [1770, 1, 254],
                                 [1780, 1, 255],
@@ -394,7 +394,7 @@ def test_events():
                                response_trig_shift=1000, event_id=event_id)
     events = raw._get_brainvision_events()
     events = events[events[:, 2] != event_id['Sync On']]
-    assert_array_equal(events, [[487, 1, 253],
+    assert_array_equal(events, [[487, 0, 253],
                                 [497, 1, 255],
                                 [1770, 1, 254],
                                 [1780, 1, 255],
@@ -413,7 +413,7 @@ def test_events():
                                    response_trig_shift=None)
     events = raw._get_brainvision_events()
     events = events[events[:, 2] != event_id['Sync On']]
-    assert_array_equal(events, [[487, 1, 253],
+    assert_array_equal(events, [[487, 0, 253],
                                 [497, 1, 255],
                                 [1770, 1, 254],
                                 [1780, 1, 255],
@@ -431,7 +431,7 @@ def test_events():
                                    response_trig_shift=None,
                                    event_id=event_id)
     events = raw._get_brainvision_events()
-    assert_array_equal(events, [[487, 1, 253],
+    assert_array_equal(events, [[487, 0, 253],
                                 [497, 1, 255],
                                 [1770, 1, 254],
                                 [1780, 1, 255],
@@ -448,6 +448,19 @@ def test_events():
     assert_raises(TypeError, read_raw_brainvision, vhdr_path, eog=eog,
                   preload=True, response_trig_shift=np.nan)
 
+    # to handle the min duration = 1 of stim trig (re)construction ...
+    events = np.array([[487, 1, 253],
+                       [497, 1, 255],
+                       [1770, 1, 254],
+                       [1780, 1, 255],
+                       [3253, 1, 254],
+                       [3263, 1, 255],
+                       [4936, 1, 253],
+                       [4946, 1, 255],
+                       [6620, 1, 254],
+                       [6630, 1, 255],
+                       [7630, 1, 5]])
+
     # Test that both response_trig_shit and event_id can be set
     read_raw_brainvision(vhdr_path, eog=eog, preload=False,
                          response_trig_shift=100, event_id=event_id)