-
Notifications
You must be signed in to change notification settings - Fork 78
Synchronous Outlet for zero-copying socket writing #153
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
89252cd
6b14a1f
2207976
13787d3
9c04b94
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,69 +1,95 @@ | ||
| #include "lsl_cpp.h" | ||
| #include <iostream> | ||
| #include <stdlib.h> | ||
| #include <time.h> | ||
| using namespace std; | ||
|
|
||
| /** | ||
| * This example program offers an 8-channel stream, float-formatted, that resembles EEG data. | ||
| * The example demonstrates also how per-channel meta-data can be specified using the .desc() field | ||
| * of the stream information object. | ||
| * | ||
| * Note that the timer used in the send loop of this program is not particularly accurate. | ||
| */ | ||
|
|
||
|
|
||
| const char *channels[] = {"C3", "C4", "Cz", "FPz", "POz", "CPz", "O1", "O2"}; | ||
|
|
||
| int main(int argc, char *argv[]) { | ||
| string name, type; | ||
| if (argc != 3) { | ||
| cout << "This opens a stream under some user-defined name and with a user-defined content " | ||
| "type." | ||
| << endl; | ||
| cout << "Please enter the stream name and the stream type (e.g. \"BioSemi EEG\" (without " | ||
| "the quotes)):" | ||
| << endl; | ||
| cin >> name >> type; | ||
| } else { | ||
| name = argv[1]; | ||
| type = argv[2]; | ||
| } | ||
|
|
||
| try { | ||
|
|
||
| // make a new stream_info (100 Hz) | ||
| lsl::stream_info info(name, type, 8, 100, lsl::cf_float32, string(name) += type); | ||
|
|
||
| // add some description fields | ||
| info.desc().append_child_value("manufacturer", "BioSemi"); | ||
| lsl::xml_element chns = info.desc().append_child("channels"); | ||
| for (int k = 0; k < 8; k++) | ||
| chns.append_child("channel") | ||
| .append_child_value("label", channels[k]) | ||
| .append_child_value("unit", "microvolts") | ||
| .append_child_value("type", "EEG"); | ||
|
|
||
| // make a new outlet | ||
| lsl::stream_outlet outlet(info); | ||
|
|
||
| // send data forever | ||
| cout << "Now sending data... " << endl; | ||
| double starttime = ((double)clock()) / CLOCKS_PER_SEC; | ||
| for (unsigned t = 0;; t++) { | ||
|
|
||
| // wait a bit and create random data | ||
| while (((double)clock()) / CLOCKS_PER_SEC < starttime + t * 0.01) | ||
| ; | ||
| float sample[8]; | ||
| for (int c = 0; c < 8; c++) sample[c] = (float)((rand() % 1500) / 500.0 - 1.5); | ||
|
|
||
| // send the sample | ||
| outlet.push_sample(sample); | ||
| } | ||
|
|
||
| } catch (std::exception &e) { cerr << "Got an exception: " << e.what() << endl; } | ||
| cout << "Press any key to exit. " << endl; | ||
| cin.get(); | ||
| return 0; | ||
| } | ||
| #include "lsl_cpp.h" | ||
| #include <iostream> | ||
| #include <stdlib.h> | ||
| #include <time.h> | ||
| #include <array> | ||
| using namespace std; | ||
|
|
||
| /** | ||
| * This example program offers an 8-channel stream, float-formatted, that resembles EEG data. | ||
| * The example demonstrates also how per-channel meta-data can be specified using the .desc() field | ||
| * of the stream information object. | ||
| * | ||
| * Note that the timer used in the send loop of this program is not particularly accurate. | ||
| */ | ||
|
|
||
|
|
||
| const char *channels[] = {"C3", "C4", "Cz", "FPz", "POz", "CPz", "O1", "O2"}; | ||
|
|
||
| int main(int argc, char *argv[]) { | ||
| string name, type; | ||
| if (argc < 3) { | ||
| cout << "This opens a stream under some user-defined name and with a user-defined content " | ||
| "type." << endl; | ||
| cout << "SendData Name Type n_channels[8] srate[100] max_buffered[360] sync[false] contig[true]" << endl; | ||
| cout << "Please enter the stream name and the stream type (e.g. \"BioSemi EEG\" (without " | ||
| "the quotes)):" | ||
| << endl; | ||
| cin >> name >> type; | ||
| } else { | ||
| name = argv[1]; | ||
| type = argv[2]; | ||
| } | ||
| int n_channels = argc > 3 ? std::stol(argv[3]) : 8; | ||
| n_channels = n_channels < 8 ? 8 : n_channels; | ||
| int samplingrate = argc > 4 ? std::stol(argv[4]) : 100; | ||
| int max_buffered = argc > 5 ? std::stol(argv[5]) : 360; | ||
| bool sync = argc > 6 ? std::stol(argv[6]) > 0 : false; | ||
| bool contig = argc > 7 ? std::stol(argv[7]) > 0 : true; | ||
|
|
||
| try { | ||
|
|
||
| // make a new stream_info (100 Hz) | ||
| lsl::stream_info info(name, type, n_channels, samplingrate, lsl::cf_float32, string(name) += type); | ||
|
|
||
| // add some description fields | ||
| info.desc().append_child_value("manufacturer", "LSL"); | ||
| lsl::xml_element chns = info.desc().append_child("channels"); | ||
| for (int k = 0; k < n_channels; k++) | ||
| chns.append_child("channel") | ||
| .append_child_value("label", k < 8 ? channels[k] : "Chan-" + std::to_string(k+1)) | ||
| .append_child_value("unit", "microvolts") | ||
| .append_child_value("type", type); | ||
|
|
||
| // make a new outlet | ||
| lsl::stream_outlet outlet(info, 0, max_buffered, sync ? transp_sync_blocking : transp_default); | ||
|
|
||
| // send data forever | ||
| cout << "Now sending data... " << endl; | ||
| double starttime = ((double)clock()) / CLOCKS_PER_SEC; | ||
|
|
||
| // Initialize 2 discontiguous data arrays. | ||
| vector<float> sample(8, 0.0); | ||
| vector<float> extra(n_channels - 8, 0.0); | ||
| if (contig) { | ||
| // If this is contiguous mode (default) then we combine the arrays. | ||
| sample.insert( | ||
| sample.end(), | ||
| make_move_iterator(extra.begin()), | ||
| make_move_iterator(extra.end())); | ||
| } | ||
| // bytes is used in !contig mode because we need to know how big each buffer is. | ||
| array<uint32_t, 2> bytes = {8 * sizeof(float), static_cast<uint32_t>((n_channels - 8) * sizeof(float))}; | ||
| for (unsigned t = 0;; t++) { | ||
|
|
||
| // wait a bit and create random data | ||
| while (((double)clock()) / CLOCKS_PER_SEC < starttime + t * 0.01) | ||
cboulay marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ; | ||
| for (int c = 0; c < 8; c++) sample[c] = (float)((rand() % 1500) / 500.0 - 1.5); | ||
tstenner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // send the sample | ||
| if (contig) | ||
| outlet.push_sample(sample); | ||
| else { | ||
| // Advanced: Push set of discontiguous buffers. | ||
| array<float *, 2> bufs = {sample.data(), extra.data()}; | ||
| outlet.push_numeric_bufs(reinterpret_cast<const char **>(const_cast<const float**>(bufs.data())), | ||
|
||
| bytes.data(), 2, lsl::local_clock(), true); | ||
| } | ||
| } | ||
|
|
||
| } catch (exception &e) { cerr << "Got an exception: " << e.what() << endl; } | ||
| cout << "Press any key to exit. " << endl; | ||
| cin.get(); | ||
| return 0; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -99,7 +99,7 @@ extern LIBLSL_C_API int32_t lsl_push_sample_vtp(lsl_outlet out, const void *data | |
| * @see lsl_push_sample_ftp | ||
| * @param out The lsl_outlet object through which to push the data. | ||
| * @param data A pointer to values to push. The number of values pointed to must be no less than the number of channels in the sample. | ||
| * @param lengths A pointer the number of elements to push for each channel (string lengths). | ||
| * @param lengths A pointer the number of elements to push for each channel (string lengths, or number of bytes). | ||
cboulay marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| */ | ||
| extern LIBLSL_C_API int32_t lsl_push_sample_buf(lsl_outlet out, const char **data, const uint32_t *lengths); | ||
| /** @copydoc lsl_push_sample_buf | ||
|
|
@@ -108,6 +108,11 @@ extern LIBLSL_C_API int32_t lsl_push_sample_buft(lsl_outlet out, const char **da | |
| /** @copydoc lsl_push_sample_buft | ||
| * @param pushthrough @see lsl_push_sample_ftp */ | ||
| extern LIBLSL_C_API int32_t lsl_push_sample_buftp(lsl_outlet out, const char **data, const uint32_t *lengths, double timestamp, int32_t pushthrough); | ||
| /** @copydoc lsl_push_sample_buftp | ||
| * @param nbufs Number of values pointed to in `data` and number of items in `lengths` -- doesn't assume one buffer | ||
| * per channel but each array in data must be longer than each item in lengths. | ||
| */ | ||
| extern LIBLSL_C_API int32_t lsl_push_sample_buftpn(lsl_outlet out, const char **data, const uint32_t *lengths, double timestamp, int32_t pushthrough, uint32_t nbufs); | ||
|
||
|
|
||
| /** Push a chunk of multiplexed samples into the outlet. One timestamp per sample is provided. | ||
| * | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -499,7 +499,7 @@ class stream_outlet { | |
| } | ||
|
|
||
| /** Push a pointer to raw numeric data as one sample into the outlet. | ||
| * This is the lowest-level function; performns no checking whatsoever. Can not be used for | ||
| * This is the lowest-level function; performs no checking whatsoever. Cannot be used for | ||
| * variable-size / string-formatted channels. | ||
| * @param sample A pointer to the raw sample data to push. | ||
| * @param timestamp Optionally the capture time of the sample, in agreement with local_clock(); | ||
|
|
@@ -512,6 +512,20 @@ class stream_outlet { | |
| lsl_push_sample_vtp(obj.get(), (sample), timestamp, pushthrough); | ||
| } | ||
|
|
||
| /** | ||
| * Push a pointer to an array of buffers of variable size as one sample into the outlet. | ||
| * | ||
| * @param bufs A pointer to an array of data buffers. | ||
| * @param bytes An array of sizes (number of bytes) of buffers in bufs. | ||
| * @param nbufs Total number of buffers. | ||
| * @param timestamp Optionally the capture time of the sample, in agreement with local_clock(); | ||
| * @param pushthrough Whether to push the sample through to the receivers immediately instead of | ||
| * concatenating with subsequent samples. | ||
| */ | ||
| void push_numeric_bufs(const char **bufs, uint32_t *bytes, uint32_t nbufs, double timestamp = 0.0, bool pushthrough = true) { | ||
|
||
| lsl_push_sample_buftpn(obj.get(), bufs, bytes, timestamp, pushthrough, nbufs); | ||
| } | ||
|
|
||
|
|
||
| // =================================================== | ||
| // === Pushing an chunk of samples into the outlet === | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.