Join data with different timestamps in MNE Raw object

I think the following code with a few adaptations could fixe your issue.
Here is some code I wrote in which I saved both EEG data and some lsl string messages in a XDF file using labrecorder.

Load the file using mnelab

import pyxdf
import pylsl
from mnelab.io import read_raw

xdf_filename = "path_to_your_datafile.xdf"

raw = read_raw(xdf_filename, stream_id=None)

Locate your streams

Basically you have a continuous data stream and a non continuous that starts on the first stimulation. If I’m correct, both have a timestamp expressed in seconds set by the LSL protocol, so you can’t just add stimulations with their absolute timestamp. The idea here is to substract the very first EEG timestamp to your stimulation stream such that you get the position of stimuli from sample 0. It is afterwards convenient to translate those stimuli as MNE annotations, with onsets expressed in seconds.

First locate them.
You must here change the match values of “type” or match using the “name” of the steam.

eeg_stream = None
msg_stream = None

streams, header = pyxdf.load_xdf(xdf_filename)
# detect the EEG stream
states_list_eeg = pyxdf.match_streaminfos(pyxdf.resolve_streams(xdf_filename),
                                          [{"type": "EEG"}])
for states_stream_id in states_list_eeg:
    for stream in streams:
        if stream["info"]["stream_id"] == states_stream_id:
            eeg_stream = stream
            print('Found eeg stream {}'.format(states_stream_id))
            break
assert eeg_stream is not None, 'EEG stream not found'

# detect the message stream
states_list_ids = pyxdf.match_streaminfos(pyxdf.resolve_streams(xdf_filename),
                                          [{"type": "msg_states"}])
for states_stream_id in states_list_ids:
    for stream in streams:
        if stream["info"]["stream_id"] == states_stream_id:
            msg_stream = stream
            print('Found message stream {}'.format(states_stream_id))
            break
assert eeg_stream is not None, 'lsl message stream not found in xdf file'

Locate the very first timestamp in the continuous EEG channel

first_samp = eeg_stream["time_stamps"][0]

use it as a correction for relative time stamp of the message channel

print('first time stamp correction {}'.format(first_samp))
onsets = msg_stream["time_stamps"] - first_samp

Manually generate and add timestamped annotations to your RawArray

descriptions = [item for sub in msg_stream["time_series"] for item in sub]
raw.annotations.append(onsets, [0] * len(onsets), descriptions)

Plot your data

raw.plot()

As you can see in my case it worked at synchronizing annotations that I knew started at timestamps 1sec and 2sec.
Please tell me if you’re successful.

ps: please note that events with duration of 0 have been ignored by mne so I’d recomment to test using [1] * len(onsets) in the durations parameter when appending annotations.