Proper way to receive an AC variable from a plugin in a chain (especially lsl2ac)?

Post Reply
steffendasenbrock
Site Admin
Posts: 12
Joined: Thu May 02, 2019 1:08 pm

Proper way to receive an AC variable from a plugin in a chain (especially lsl2ac)?

Post by steffendasenbrock » Thu Aug 12, 2021 9:35 am

Hi :)

I've been playing around a bit with example6.cpp from the Developer manual to get to know how to work with AC variables. I've managed to create an AC variable by declaring it as member of the class example6_t:

Code: Select all

comm_var_t test_ac;
And setting up data_type, num_entries, stride and data as well as register it in the constructor.

Code: Select all

test_ac.data_type = MHA_AC_FLOAT;
test_ac.num_entries = 2;
test_ac.stride = 1; 
test_ac.data = test;

ac.insert_var( ac.handle, "testac", test_ac );
The float variable test is filled in the process function.

Now I was wondering how to get an AC variable from an other plugin. For this I've put the original plugin example6 before my own plugin:

Code: Select all

mha.algos=[ example6 steffencov acmon ]
The plugin example6 creates an AC variable called "example6_rmslev" which I tried to catch in my own plugin steffencov using

Code: Select all

 ac.get_var(ac.handle, "example6_rmslev",&new_ac_var); 
and register it as a new AC variable to test if it worked:

Code: Select all

ac.insert_var( ac.handle, "test3", new_ac_var );
And it did. The AC variable example6_rmslev had the same value as test3. Then I wanted to try the same thing, but instead of having example6 switched before my own plugin, I've used lsl2ac to catch e.g. the clock corrections of a test stream e.g.

Code: Select all

    ac.get_var(ac.handle, "SimpleStream_tc",&new_ac_var); 
and

Code: Select all

    ac.insert_var( ac.handle, "test3", new_ac_var ); 
But this did not work - in this case test3 is just an empty 0x1 variable:

Code: Select all

# complete list of variables
# vector<string> (monitor)
mha.acmon.varlist = [SimpleStream SimpleStream_new SimpleStream_tc SimpleStream_ts test3]

# variable dimensions in AC space
# vector<string> (monitor)
mha.acmon.dimensions = [1x480 1x1 1x1 1x1 0x1]
Do you know why this could be?
I will attach my (rather messy) C++ code and the configuration I've used for testing this.

Best regards,
Steffen
Attachments
config_and_cpp_file.zip
(4.22 KiB) Downloaded 3180 times

steffendasenbrock
Site Admin
Posts: 12
Joined: Thu May 02, 2019 1:08 pm

Re: Proper way to receive an AC variable from a plugin in a chain (especially lsl2ac)?

Post by steffendasenbrock » Thu Aug 19, 2021 12:39 pm

I found a second way to access the value of an AC variable from another plugin (in this case rmslevel), using:

Code: Select all

 mha_wave_t* example6_t::process(mha_wave_t* wave)
{
    poll_config();
    store_ac_single = MHA_AC::get_var_float(ac,"rmslevel_level");
    return wave;
} 
store_ac_single is a float variable, which means I can simply do stuff like this:

Code: Select all

 store_ac_single = store_ac_single+1; 
This works also with vectors (when setting nchannels_in = 2 rmslevel_level will be a 1x2 vector):

Code: Select all

# vector<float> (monitor)
mha.acmon.rmslevel_level = [0.00896696746 5.30756188e-05]
In this case one can declare a vector which should later hold the values

Code: Select all

    std::vector<float> store_ac_single;
and use:

Code: Select all

    store_ac_single = MHA_AC::get_var_vfloat(ac,"rmslevel_level_db");
to store store them. The problem here is that this does not work with the AC vars exported from lsl2ac.

When trying to do the same to an LSL stream, e.g. trying to catch the values of a time series using

Code: Select all

 store_ac_single = MHA_AC::get_var_vfloat(ac,"Mobi_EEG_030105"); 
one gets:

Code: Select all

mha [1] MHA Error: (mhapluginloader) Error in module "mhachain:mhachain":
(mhapluginloader) Error in module "steffencov:steffencov":
(mha_algo_comm) Algorithm communication variable Mobi_EEG_030105 has unexpected data type 4
although acmon says it's a vector<float>, not sure if I misunderstand something here.

Code: Select all

# vector<float> (monitor)
mha.acmon.Mobi_EEG_030105 = [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
Using

Code: Select all

 store_ac_single = MHA_AC::get_var_float(ac,"Mobi_EEG_030105_tc"); 
(float instead of vfloat)

Works just fine since it is a scalar value.

So my question would be how do I ready in the AC variable time series values which are not scalars but vectors?

Minimal example will be attached.
Attachments
return_AC_vectors.zip
(3.92 KiB) Downloaded 3156 times

tobiasherzke
Posts: 119
Joined: Mon Jun 24, 2019 12:51 pm

Re: Proper way to receive an AC variable from a plugin in a chain (especially lsl2ac)?

Post by tobiasherzke » Sun Aug 29, 2021 11:17 am

steffendasenbrock wrote:
Thu Aug 12, 2021 9:35 am
Then I wanted to try the same thing, but instead of having example6 switched before my own plugin, I've used lsl2ac to catch e.g. the clock corrections of a test stream e.g.

Code: Select all

    ac.get_var(ac.handle, "SimpleStream_tc",&new_ac_var);
    ac.insert_var( ac.handle, "test3", new_ac_var );
But this did not work - in this case test3 is just an empty 0x1 variable:
The above code needs to execute every time prepare() or process() executes.

Please see the developer manual, section 3.4 "Communication between algorithms", especially 3.4.1 "Detailed Description", and there especially the best practices following the sentence "As a developer of openMHA plugin(s), please observe the following best practices in plugins using AC variables".

As you can see from the best practices, you should really not do it like in your example because there you are violating the best practice "memory used for storing AC variable values is allocated and owned by the publishing plugin". But I understand that you are only doing this wile learning about AC variables and will use a safer approach in practice.

steffendasenbrock
Site Admin
Posts: 12
Joined: Thu May 02, 2019 1:08 pm

Re: Proper way to receive an AC variable from a plugin in a chain (especially lsl2ac)?

Post by steffendasenbrock » Tue Sep 07, 2021 2:01 pm

Thank you for your answer!

executing the code in process() as well as in prepare() solved the issue. Yet I'm not sure if I'm still violating some of the best practices. In my case I tried to calculate the mean value all elements of the time series AC variable exported by lsl2ac, e.g. SimpleStream and create a new AC variable. For this I use the following function:

Code: Select all

void example6_t::calculate_mean_value() {
	// Index for going through data chunk
	unsigned int fr;
	// Get EEG/LSL data
    store_ac_single = MHA_AC::get_var_waveform(ac,lsl_time_series.data);
    // Set mean value to 0
    mean_value = 0.0;
    // Sum up all the values of time series
    for(fr = 0; fr < store_ac_single.num_frames*store_ac_single.num_channels; fr++){
        mean_value += store_ac_single.buf[fr];
    }
    // Normalize by divding by number of elements
    mean_value = mean_value/(store_ac_single.num_frames*store_ac_single.num_channels);
    // Insert mean value as new AC variable
    ac.insert_var_float( ac.handle, "Mean_value", &mean_value );
}
This function is executed in the prepare as well as in the process function. Should I only execute parts of the function, meaning only:

Code: Select all

store_ac_single = MHA_AC::get_var_waveform(ac,lsl_time_series.data);
ac.insert_var_float( ac.handle, "Mean_value", &mean_value );
?

What exactly is meant with "memory used for storing AC variable values is allocated and owned by the publishing
plugin and needs to remain valid until the next call to process() or release() of the
same plugin."?

I will attach my code and the configs.
Attachments
question.zip
(2.88 KiB) Downloaded 3197 times

tobiasherzke
Posts: 119
Joined: Mon Jun 24, 2019 12:51 pm

Re: Proper way to receive an AC variable from a plugin in a chain (especially lsl2ac)?

Post by tobiasherzke » Fri Sep 10, 2021 3:41 pm

Looking only at the code snippets in the forum post: look fine to me.

Post Reply