Split up indatahandler with python callback in part and part that calls the Python function. Threading is now handled using a thread pool. Some bugfixes

This commit is contained in:
Anne de Jong 2022-08-14 21:00:22 +02:00
parent 1e8b18aabe
commit c75f0dddc5
13 changed files with 315 additions and 195 deletions

View File

@ -1,3 +1,4 @@
#define DEBUGTRACE_ENABLED
#include "debugtrace.hpp" #include "debugtrace.hpp"
#include "lasp_daqconfig.h" #include "lasp_daqconfig.h"

View File

@ -1,4 +1,4 @@
/* #define DEBUGTRACE_ENABLED */ #define DEBUGTRACE_ENABLED
#include "debugtrace.hpp" #include "debugtrace.hpp"
#include "lasp_daqconfig.h" #include "lasp_daqconfig.h"
@ -7,8 +7,6 @@
#include <cassert> #include <cassert>
#include <stdexcept> #include <stdexcept>
#define MAX_DEV_COUNT_PER_API 20
using std::vector; using std::vector;
vector<DaqApi> DaqApi::getAvailableApis() { vector<DaqApi> DaqApi::getAvailableApis() {

View File

@ -1,6 +1,8 @@
#define DEBUGTRACE_ENABLED
#include "debugtrace.hpp"
#include "lasp_rtaudiodaq.h" #include "lasp_rtaudiodaq.h"
#if LASP_HAS_RTAUDIO == 1 #if LASP_HAS_RTAUDIO == 1
#include "debugtrace.hpp"
#include "lasp_daq.h" #include "lasp_daq.h"
#include <RtAudio.h> #include <RtAudio.h>
#include <atomic> #include <atomic>

View File

@ -1,5 +1,7 @@
#include "lasp_streammgr.h" #define DEBUGTRACE_ENABLED
#include "debugtrace.hpp" #include "debugtrace.hpp"
#include "lasp_thread.h"
#include "lasp_streammgr.h"
#include <algorithm> #include <algorithm>
#include <assert.h> #include <assert.h>
#include <functional> #include <functional>
@ -37,6 +39,7 @@ InDataHandler::~InDataHandler() {
StreamMgr &StreamMgr::getInstance() { StreamMgr &StreamMgr::getInstance() {
DEBUGTRACE_ENTER; DEBUGTRACE_ENTER;
getPool();
static StreamMgr mgr; static StreamMgr mgr;
return mgr; return mgr;
} }
@ -198,6 +201,29 @@ void StreamMgr::startStream(const DeviceInfo &devinfo,
_outputStream = std::move(daq); _outputStream = std::move(daq);
} }
} }
void StreamMgr::stopStream(const StreamType t) {
switch (t) {
case (StreamType::input): {
if (!_inputStream) {
throw std::runtime_error("Input stream is not running");
}
_inputStream = nullptr;
} break;
case (StreamType::output): {
if (_inputStream && _inputStream->duplexMode()) {
_inputStream = nullptr;
} else {
if (!_outputStream) {
throw std::runtime_error("Output stream is not running");
}
_outputStream = nullptr;
} // end else
} break;
default:
throw std::runtime_error("BUG");
break;
}
}
void StreamMgr::addInDataHandler(InDataHandler &handler) { void StreamMgr::addInDataHandler(InDataHandler &handler) {
std::scoped_lock<std::mutex> lck(_inDataHandler_mtx); std::scoped_lock<std::mutex> lck(_inDataHandler_mtx);

View File

@ -155,7 +155,7 @@ public:
* *
* @param stype The stream type to stop. * @param stype The stream type to stop.
*/ */
void stopStream(StreamType stype); void stopStream(const StreamType stype);
/** /**
* @brief Stop and delete all streams. Also called on destruction of the * @brief Stop and delete all streams. Also called on destruction of the

View File

@ -11,12 +11,15 @@ set(lasp_dsp_files
lasp_thread.cpp lasp_thread.cpp
lasp_timebuffer.cpp lasp_timebuffer.cpp
lasp_slm.cpp lasp_slm.cpp
lasp_threadedindatahandler.cpp
) )
add_library(lasp_dsp_lib STATIC ${lasp_dsp_files}) add_library(lasp_dsp_lib STATIC ${lasp_dsp_files})
target_compile_definitions(lasp_dsp_lib PUBLIC "-DARMA_DONT_USE_WRAPPER") target_compile_definitions(lasp_dsp_lib PUBLIC "-DARMA_DONT_USE_WRAPPER")
target_link_libraries(lasp_dsp_lib PRIVATE ${BLAS_LIBRARIES}) target_link_libraries(lasp_dsp_lib PRIVATE ${BLAS_LIBRARIES})
target_include_directories(lasp_dsp_lib INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(lasp_dsp_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_include_directories(lasp_dsp_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../device)

View File

@ -1,9 +1,25 @@
#define DEBUGTRACE_ENABLED
#include "lasp_thread.h" #include "lasp_thread.h"
#include "BS_thread_pool.hpp" #include "BS_thread_pool.hpp"
#include "debugtrace.hpp"
#include <memory>
BS::thread_pool& getPool() { /**
static BS::thread_pool pool; * @brief It seems to work much better in cooperation with Pybind11 when this
* singleton is implemented with a unique_ptr.
return pool; */
std::unique_ptr<BS::thread_pool> _static_storage_threadpool;
void destroyThreadPool() {
DEBUGTRACE_ENTER;
_static_storage_threadpool = nullptr;
}
BS::thread_pool &getPool() {
/* DEBUGTRACE_ENTER; */
if (!_static_storage_threadpool) {
DEBUGTRACE_PRINT("Creating new thread pool");
_static_storage_threadpool = std::make_unique<BS::thread_pool>();
}
return *_static_storage_threadpool;
} }

View File

@ -9,3 +9,11 @@
* @return Thread pool ref. * @return Thread pool ref.
*/ */
BS::thread_pool& getPool(); BS::thread_pool& getPool();
/**
* @brief The global thread pool is stored in a unique_ptr, so in normal C++
* code the thread pool is deleted at the end of main(). However this does not
* hold when LASP code is run
*/
void destroyThreadPool();

View File

@ -0,0 +1,65 @@
#define DEBUGTRACE_ENABLED
#include "lasp_threadedindatahandler.h"
#include "debugtrace.hpp"
#include "lasp_thread.h"
#include <future>
#include <thread>
using namespace std::literals::chrono_literals;
ThreadedInDataHandler::ThreadedInDataHandler(StreamMgr &mgr)
: InDataHandler(mgr) {
DEBUGTRACE_ENTER;
// Initialize thread pool, if not already done
getPool();
}
bool ThreadedInDataHandler::inCallback(const DaqData &daqdata) {
/* DEBUGTRACE_ENTER; */
if (!_lastCallbackResult) {
return false;
}
dataqueue.push(daqdata);
auto &pool = getPool();
if (!_thread_running && (!_stopThread) && _lastCallbackResult) {
pool.push_task(&ThreadedInDataHandler::threadFcn, this);
}
return _lastCallbackResult;
}
ThreadedInDataHandler::~ThreadedInDataHandler() {
DEBUGTRACE_ENTER;
_stopThread = true;
// Then wait in steps for the thread to stop running.
while (_thread_running) {
std::this_thread::sleep_for(10us);
}
}
void ThreadedInDataHandler::threadFcn() {
/* DEBUGTRACE_ENTER; */
_thread_running = true;
DaqData d{};
if (dataqueue.pop(d) && !_stopThread) {
// Call inCallback_threaded
if (inCallback_threaded(d) == false) {
_lastCallbackResult = false;
_thread_running = false;
return;
}
}
_lastCallbackResult = true;
_thread_running = false;
}

View File

@ -0,0 +1,36 @@
#pragma once
#include <boost/lockfree/spsc_queue.hpp>
#include "lasp_streammgr.h"
const us RINGBUFFER_SIZE = 1024;
class ThreadedInDataHandler: public InDataHandler {
/**
* @brief The queue used to push elements to the handling thread.
*/
boost::lockfree::spsc_queue<DaqData,
boost::lockfree::capacity<RINGBUFFER_SIZE>>
dataqueue;
std::atomic<bool> _thread_running{false};
std::atomic<bool> _stopThread{false};
std::atomic<bool> _lastCallbackResult{true};
void threadFcn();
public:
ThreadedInDataHandler(StreamMgr& mgr);
~ThreadedInDataHandler();
/**
* @brief Pushes a copy of the daqdata to the thread queue and returns
*
* @param daqdata the daq info to push
*
* @return true, to continue with sampling.
*/
virtual bool inCallback(const DaqData &daqdata) override;
virtual bool inCallback_threaded(const DaqData&) = 0;
};

View File

@ -1,8 +1,8 @@
#define DEBUGTRACE_ENABLED
#include "debugtrace.hpp" #include "debugtrace.hpp"
#include "lasp_streammgr.h" #include "lasp_streammgr.h"
#include "lasp_threadedindatahandler.h"
#include <atomic> #include <atomic>
#include <boost/lockfree/policies.hpp>
#include <boost/lockfree/spsc_queue.hpp>
#include <chrono> #include <chrono>
#include <pybind11/buffer_info.h> #include <pybind11/buffer_info.h>
#include <pybind11/cast.h> #include <pybind11/cast.h>
@ -17,8 +17,6 @@ using namespace std::literals::chrono_literals;
using std::cerr; using std::cerr;
using std::endl; using std::endl;
const us RINGBUFFER_SIZE = 1024;
namespace py = pybind11; namespace py = pybind11;
/** /**
@ -30,7 +28,7 @@ namespace py = pybind11;
* *
* @return Numpy array * @return Numpy array
*/ */
template <typename T> py::array_t<T> getPyArray(DaqData &d) { template <typename T> py::array_t<T> getPyArray(const DaqData &d) {
// https://github.com/pybind/pybind11/issues/323 // https://github.com/pybind/pybind11/issues/323
// //
// When a valid object is passed as 'base', it tells pybind not to take // When a valid object is passed as 'base', it tells pybind not to take
@ -49,16 +47,17 @@ template <typename T> py::array_t<T> getPyArray(DaqData &d) {
return py::array_t<T>( return py::array_t<T>(
py::array::ShapeContainer({d.nframes, d.nchannels}), // Shape py::array::ShapeContainer({d.nframes, d.nchannels}), // Shape
//
py::array::StridesContainer( // Strides
{sizeof(T),
sizeof(T) * d.nframes}), // Strides (in bytes) for each index
(T *)d.raw_ptr(), // Pointer to buffer py::array::StridesContainer( // Strides
{sizeof(T),
sizeof(T) * d.nframes}), // Strides (in bytes) for each index
reinterpret_cast<T *>(
const_cast<DaqData &>(d).raw_ptr()), // Pointer to buffer
dummyDataOwner // As stated above, now Numpy does not take ownership of dummyDataOwner // As stated above, now Numpy does not take ownership of
// the data pointer. // the data pointer.
); );
} }
/** /**
@ -66,130 +65,79 @@ template <typename T> py::array_t<T> getPyArray(DaqData &d) {
* buffer of sample data. The Python callback is called from a different * buffer of sample data. The Python callback is called from a different
* thread, using a Numpy argument as * thread, using a Numpy argument as
*/ */
class PyIndataHandler : public InDataHandler { class PyIndataHandler : public ThreadedInDataHandler {
/** /**
* @brief The callback function that is called. * @brief The callback function that is called.
*/ */
py::function cb; py::function cb;
/**
* @brief The thread that is handling callbacks from the queue
*/
std::thread pythread;
/**
* @brief The queue used to push elements to the handling thread.
*/
boost::lockfree::spsc_queue<DaqData,
boost::lockfree::capacity<RINGBUFFER_SIZE>>
dataqueue;
std::atomic<bool> stopThread{false};
public: public:
PyIndataHandler(StreamMgr &mgr, py::function cb)
: ThreadedInDataHandler(mgr), cb(cb) {
DEBUGTRACE_ENTER;
/// TODO: Note that if start() throws an exception, which means that the
/// destructor of PyIndataHandler is not called and the thread destructor
/// calls terminate(). It is a kind of rude way to crash, but it is also
/// *very* unlikely to happen, as start() does only add a reference to this
/// handler to a list in the stream mgr.
start();
}
~PyIndataHandler() { ~PyIndataHandler() {
DEBUGTRACE_ENTER; DEBUGTRACE_ENTER;
stop(); stop();
stopThread = true;
if(pythread.joinable()) {
pythread.join();
}
} }
PyIndataHandler(StreamMgr &mgr, py::function cb)
: InDataHandler(mgr), cb(cb),
pythread(&PyIndataHandler::threadfcn, this) {
DEBUGTRACE_ENTER;
/// TODO: Note that if start() throws an exception, which means that the
/// destructor of PyIndataHandler is not called and the thread destructor
/// calls terminate(). It is a kind of rude way to crash, but it is also
/// *very* unlikely to happen, as start() does only add a reference to this
/// handler to a list in the stream mgr.
start();
}
/** /**
* @brief Reads from the * @brief Reads from the buffer
*/ */
void threadfcn() { bool inCallback_threaded(const DaqData &d) {
/* DEBUGTRACE_ENTER; */ /* DEBUGTRACE_ENTER; */
using DataType = DataTypeDescriptor::DataType; using DataType = DataTypeDescriptor::DataType;
DaqData d{}; py::gil_scoped_acquire acquire;
try {
py::array binfo;
switch (d.dtype) {
case (DataType::dtype_int8):
binfo = getPyArray<int8_t>(d);
break;
case (DataType::dtype_int16):
binfo = getPyArray<int16_t>(d);
break;
case (DataType::dtype_int32):
binfo = getPyArray<int32_t>(d);
break;
case (DataType::dtype_fl32):
binfo = getPyArray<float>(d);
break;
case (DataType::dtype_fl64):
binfo = getPyArray<double>(d);
break;
default:
throw std::runtime_error("BUG");
} // End of switch
while (!stopThread) { py::object bool_val = cb(binfo);
if (dataqueue.pop(d)) { bool res = bool_val.cast<bool>();
/* DEBUGTRACE_PRINT("pop() returned true"); */ if (!res)
return false;
py::gil_scoped_acquire acquire; } catch (py::error_already_set &e) {
try { cerr << "ERROR: Python raised exception from callback function: ";
py::array binfo; cerr << e.what() << endl;
switch (d.dtype) { return false;
case (DataType::dtype_int8):
binfo = getPyArray<uint8_t>(d);
break;
case (DataType::dtype_int16):
binfo = getPyArray<uint16_t>(d);
break;
case (DataType::dtype_int32):
binfo = getPyArray<uint32_t>(d);
break;
case (DataType::dtype_fl32):
binfo = getPyArray<float>(d);
break;
case (DataType::dtype_fl64):
binfo = getPyArray<double>(d);
break;
default:
throw std::runtime_error("BUG");
} // End of switch
py::object bool_val = cb(binfo); } catch (py::cast_error &e) {
bool res = bool_val.cast<bool>(); cerr << e.what() << endl;
if (!res) cerr << "ERROR: Python callback does not return boolean value." << endl;
stopThread = true; return false;
} catch (py::error_already_set &e) {
cerr << "ERROR: Python raised exception from callback function: ";
cerr << e.what() << endl;
stopThread = true;
} catch (py::cast_error &e) {
cerr << e.what() << endl;
cerr << "ERROR: Python callback does not return boolean value."
<< endl;
stopThread = true;
}
}
// When there is nothing to pop from the queue, just sleep for a some
// time.
else {
std::this_thread::sleep_for(2ms);
}
} // end of while loop
} // end of threadfcn
/**
* @brief Pushes a copy of the daqdata to the thread queuue and returns
*
* @param daqdata the daq info to push
*
* @return true, to continue with sampling.
*/
virtual bool inCallback(const DaqData &daqdata) override {
/* DEBUGTRACE_ENTER; */
if (!stopThread) {
if (!dataqueue.push(daqdata)) {
stopThread = true;
cerr << "Pushing DaqData object failed. Probably the ringbuffer is "
"full. Try reducing the load"
<< endl;
}
} }
return true; return true;
} }
}; };
void init_datahandler(py::module &m) { void init_datahandler(py::module &m) {
py::class_<PyIndataHandler> h(m, "InDataHandler"); py::class_<PyIndataHandler> h(m, "InDataHandler");

View File

@ -18,7 +18,7 @@ void init_streammgr(py::module &m) {
.export_values(); .export_values();
smgr.def("startStream", &StreamMgr::startStream); smgr.def("startStream", &StreamMgr::startStream);
smgr.def("stopStream", &StreamMgr::startStream); smgr.def("stopStream", &StreamMgr::stopStream);
smgr.def_static("getInstance", []() { smgr.def_static("getInstance", []() {
return std::unique_ptr<StreamMgr, py::nodelete>(&StreamMgr::getInstance()); return std::unique_ptr<StreamMgr, py::nodelete>(&StreamMgr::getInstance());
}); });

View File

@ -3,6 +3,7 @@
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 1, "execution_count": 1,
"id": "b0d15138",
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
@ -12,27 +13,15 @@
"make: Entering directory '/home/anne/wip/mycode/lasp'\n", "make: Entering directory '/home/anne/wip/mycode/lasp'\n",
"make[1]: Entering directory '/home/anne/wip/mycode/lasp'\n", "make[1]: Entering directory '/home/anne/wip/mycode/lasp'\n",
"make[2]: Entering directory '/home/anne/wip/mycode/lasp'\n", "make[2]: Entering directory '/home/anne/wip/mycode/lasp'\n",
"\u001b[35m\u001b[1mConsolidate compiler generated dependencies of target lasp_dsp_lib\u001b[0m\n",
"make[2]: Leaving directory '/home/anne/wip/mycode/lasp'\n", "make[2]: Leaving directory '/home/anne/wip/mycode/lasp'\n",
"[ 26%] Built target lasp_dsp_lib\n", "[ 26%] Built target lasp_dsp_lib\n",
"make[2]: Entering directory '/home/anne/wip/mycode/lasp'\n", "make[2]: Entering directory '/home/anne/wip/mycode/lasp'\n",
"\u001b[35m\u001b[1mScanning dependencies of target lasp_device_lib\u001b[0m\n", "\u001b[35m\u001b[1mConsolidate compiler generated dependencies of target lasp_device_lib\u001b[0m\n",
"make[2]: Leaving directory '/home/anne/wip/mycode/lasp'\n",
"make[2]: Entering directory '/home/anne/wip/mycode/lasp'\n",
"[ 31%] \u001b[32mBuilding CXX object src/lasp/device/CMakeFiles/lasp_device_lib.dir/lasp_rtaudiodaq.cpp.o\u001b[0m\n",
"\u001b[01m\u001b[K/home/anne/wip/mycode/lasp/src/lasp/device/lasp_rtaudiodaq.cpp:\u001b[m\u001b[K In lambda function:\n",
"\u001b[01m\u001b[K/home/anne/wip/mycode/lasp/src/lasp/device/lasp_rtaudiodaq.cpp:258:20:\u001b[m\u001b[K \u001b[01;35m\u001b[Kwarning: \u001b[m\u001b[Kvariable \u001b[01m\u001b[Kstat\u001b[m\u001b[K set but not used [\u001b[01;35m\u001b[K-Wunused-but-set-variable\u001b[m\u001b[K]\n",
" 258 | StreamStatus \u001b[01;35m\u001b[Kstat\u001b[m\u001b[K = _streamStatus;\n",
" | \u001b[01;35m\u001b[K^~~~\u001b[m\u001b[K\n",
"\u001b[01m\u001b[K/home/anne/wip/mycode/lasp/src/lasp/device/lasp_rtaudiodaq.cpp:\u001b[m\u001b[K In member function \u001b[01m\u001b[Kint RtAudioDaq::streamCallback(void*, void*, unsigned int, double, RtAudioStreamStatus)\u001b[m\u001b[K:\n",
"\u001b[01m\u001b[K/home/anne/wip/mycode/lasp/src/lasp/device/lasp_rtaudiodaq.cpp:250:36:\u001b[m\u001b[K \u001b[01;35m\u001b[Kwarning: \u001b[m\u001b[Kunused parameter \u001b[01m\u001b[KstreamTime\u001b[m\u001b[K [\u001b[01;35m\u001b[K-Wunused-parameter\u001b[m\u001b[K]\n",
" 250 | unsigned int nFrames, \u001b[01;35m\u001b[Kdouble streamTime\u001b[m\u001b[K,\n",
" | \u001b[01;35m\u001b[K~~~~~~~^~~~~~~~~~\u001b[m\u001b[K\n",
"make[2]: Leaving directory '/home/anne/wip/mycode/lasp'\n", "make[2]: Leaving directory '/home/anne/wip/mycode/lasp'\n",
"[ 63%] Built target lasp_device_lib\n", "[ 63%] Built target lasp_device_lib\n",
"make[2]: Entering directory '/home/anne/wip/mycode/lasp'\n", "make[2]: Entering directory '/home/anne/wip/mycode/lasp'\n",
"make[2]: Leaving directory '/home/anne/wip/mycode/lasp'\n", "\u001b[35m\u001b[1mConsolidate compiler generated dependencies of target lasp_cpp\u001b[0m\n",
"make[2]: Entering directory '/home/anne/wip/mycode/lasp'\n",
"[ 68%] \u001b[32m\u001b[1mLinking CXX shared module lasp_cpp.cpython-38-x86_64-linux-gnu.so\u001b[0m\n",
"make[2]: Leaving directory '/home/anne/wip/mycode/lasp'\n", "make[2]: Leaving directory '/home/anne/wip/mycode/lasp'\n",
"[100%] Built target lasp_cpp\n", "[100%] Built target lasp_cpp\n",
"make[1]: Leaving directory '/home/anne/wip/mycode/lasp'\n", "make[1]: Leaving directory '/home/anne/wip/mycode/lasp'\n",
@ -47,8 +36,20 @@
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 2, "execution_count": 2,
"id": "1787e24c",
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2022-07-20 22:04:50+0200 DebugTrace-cpp 2.0.0a2 (g++ 12.1.0)\n",
"2022-07-20 22:04:50+0200 \n",
"2022-07-20 22:04:50+0200 Enter fillUlDaqDeviceInfo (lasp_uldaq.cpp: 514)\n",
"2022-07-20 22:04:51+0200 Leave fillUlDaqDeviceInfo (lasp_uldaq.cpp)\n"
]
}
],
"source": [ "source": [
"import lasp\n", "import lasp\n",
"ds = lasp.DeviceInfo.getDeviceInfo()" "ds = lasp.DeviceInfo.getDeviceInfo()"
@ -57,6 +58,7 @@
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 3, "execution_count": 3,
"id": "cb4ab7d8",
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
@ -74,17 +76,26 @@
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 4, "execution_count": 4,
"id": "22ae99b1",
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"0: Camera Mono\n", "0: Monitor of Starship/Matisse HD Audio Controller Analog Stereo\n",
"1: Starship/Matisse HD Audio Controller Analog Stereo\n", "1: Starship/Matisse HD Audio Controller Analog Stereo\n",
"2: Baffin HDMI/DP Audio [Radeon RX 550 640SP / RX 560/560X] Digital Stereo (HDMI)\n", "2: GP108 High Definition Audio Controller Digital Stereo (HDMI)\n",
"3: Monitor of Baffin HDMI/DP Audio [Radeon RX 550 640SP / RX 560/560X] Digital Stereo (HDMI)\n", "3: Monitor of GP108 High Definition Audio Controller Digital Stereo (HDMI)\n",
"4: Monitor of Starship/Matisse HD Audio Controller Analog Stereo\n" "4: default\n",
"5: hw:HDA NVidia,3\n",
"6: hw:HDA NVidia,7\n",
"7: hw:HDA NVidia,8\n",
"8: hw:HDA NVidia,9\n",
"9: hw:HDA NVidia,10\n",
"10: hw:HD-Audio Generic,0\n",
"11: hw:HD-Audio Generic,1\n",
"12: hw:HD-Audio Generic,2\n"
] ]
} }
], ],
@ -97,6 +108,7 @@
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 5, "execution_count": 5,
"id": "47385b02",
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@ -107,56 +119,28 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 6, "execution_count": 59,
"id": "d12f84b7",
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
"data": { "name": "stdout",
"text/plain": [ "output_type": "stream",
"2" "text": [
] "Out channels: 2\n",
}, "In channels: 0\n"
"execution_count": 6, ]
"metadata": {},
"output_type": "execute_result"
} }
], ],
"source": [ "source": [
"d.noutchannels" "print('Out channels:',d.noutchannels)\n",
"print('In channels:',d.ninchannels)"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 7, "execution_count": 52,
"metadata": {}, "id": "902ce309",
"outputs": [],
"source": [
"#daq = lasp.Daq.createDaq(d, config)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"2"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"d.ninchannels"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@ -165,16 +149,38 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 15, "execution_count": 55,
"id": "b209294b",
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2022-07-20 22:07:02+0200 \n",
"2022-07-20 22:07:02+0200 Enter createDaq (lasp_daq.cpp: 18)\n",
"2022-07-20 22:07:02+0200 | Enter Daq (lasp_daq.cpp: 39)\n",
"2022-07-20 22:07:02+0200 | Leave Daq (lasp_daq.cpp)\n",
"2022-07-20 22:07:02+0200 | \n",
"2022-07-20 22:07:02+0200 | Enter RtAudioDaq (lasp_rtaudiodaq.cpp: 131)\n",
"2022-07-20 22:07:02+0200 | Leave RtAudioDaq (lasp_rtaudiodaq.cpp)\n",
"2022-07-20 22:07:02+0200 Leave createDaq (lasp_daq.cpp)\n",
"2022-07-20 22:07:02+0200 isInput = false\n",
"2022-07-20 22:07:02+0200 isOutput = true\n",
"2022-07-20 22:07:02+0200 \n",
"2022-07-20 22:07:02+0200 Enter start (lasp_rtaudiodaq.cpp: 211)\n",
"2022-07-20 22:07:02+0200 Leave start (lasp_rtaudiodaq.cpp)\n"
]
}
],
"source": [ "source": [
"mgr.startStream(d, config)" "mgr.startStream(d, config)"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 11, "execution_count": 27,
"id": "0830ffb5",
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@ -183,7 +189,8 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 16, "execution_count": 60,
"id": "a7fddc19",
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@ -192,26 +199,36 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 13, "execution_count": 57,
"id": "f4610574",
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"sine = lasp.Sine(1000)" "sine = lasp.Sine(200)\n",
"sine.setLevel(-20, True)\n",
"mgr.setSiggen(sine)"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 14, "execution_count": 21,
"id": "d11c7dae",
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": []
"mgr.setSiggen(sine)" },
] {
"cell_type": "code",
"execution_count": null,
"id": "0eeb2311",
"metadata": {},
"outputs": [],
"source": []
} }
], ],
"metadata": { "metadata": {
"kernelspec": { "kernelspec": {
"display_name": "Python 3 (ipykernel)", "display_name": "Python 3",
"language": "python", "language": "python",
"name": "python3" "name": "python3"
}, },
@ -225,7 +242,7 @@
"name": "python", "name": "python",
"nbconvert_exporter": "python", "nbconvert_exporter": "python",
"pygments_lexer": "ipython3", "pygments_lexer": "ipython3",
"version": "3.8.10" "version": "3.10.5"
} }
}, },
"nbformat": 4, "nbformat": 4,