mirror of
https://github.com/JvanKatwijk/dab-cmdline
synced 2025-10-05 23:52:50 +02:00
586 lines
17 KiB
C++
586 lines
17 KiB
C++
#
|
|
/*
|
|
* Copyright (C) 2017
|
|
* Jan van Katwijk (J.vanKatwijk@gmail.com)
|
|
* Lazy Chair Computing
|
|
*
|
|
* This file is the python wrapper for the DAB-library.
|
|
* DAB-library is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* DAB-library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with DAB-library, if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
|
|
#include <Python.h>
|
|
#include <numpy/arrayobject.h>
|
|
#include "dab-api.h"
|
|
#include "band-handler.h"
|
|
#ifdef HAVE_SDRPLAY
|
|
#include "sdrplay-handler.h"
|
|
#elif HAVE_AIRSPY
|
|
#include "airspy-handler.h"
|
|
#elif HAVE_RTLSDR
|
|
#include "rtlsdr-handler.h"
|
|
#else
|
|
#include "device-handler.h"
|
|
#endif
|
|
//
|
|
// wrapper for the python interface to the dab-library
|
|
// ensure that the function names are not mangled
|
|
//
|
|
extern "C" {
|
|
PyObject *dabInit_p (PyObject *self, PyObject *args);
|
|
PyObject *dabExit_p (PyObject *self, PyObject *args);
|
|
PyObject *dabReset_p (PyObject *self, PyObject *args);
|
|
PyObject *dabStop_p (PyObject *self, PyObject *args);
|
|
PyObject *dabService_p (PyObject *self, PyObject *args);
|
|
PyObject *dabgetSId_p (PyObject *self, PyObject *args);
|
|
PyObject *PyInit_libdab_lib (void);
|
|
}
|
|
//
|
|
// deviceHandler is local to the python interface
|
|
deviceHandler *theDevice;
|
|
//
|
|
// We need to define 'C' callback functions, whose purpose
|
|
// is to call the appropriate Python function
|
|
// We keep it simple abd implement only a few callback functions,
|
|
// the ones we think minimally needed
|
|
//
|
|
// typedef void (*syncsignal_t) (bool, // time synced or not
|
|
// void *);
|
|
// typedef void (*ensemblename_t) (std::string, // name
|
|
// int32_t, // Hex code
|
|
// void *);
|
|
// typedef void (*programname_t) (std::string, // name
|
|
// int32_t, // Id
|
|
// void *);
|
|
// typedef void (*audioOut_t) (int16_t *, // buffer
|
|
// int32_t, // size
|
|
// int32_t, // samplerate
|
|
// bool, // stereo
|
|
// void *); // context
|
|
// typedef void (*dataOut_t) (std::string,
|
|
// void *);
|
|
// typedef void (*bytesOut_t) (uint8_t *,
|
|
// int16_t,
|
|
// void *);
|
|
// typedef void (*systemdata_t) (bool,
|
|
// int16_t,
|
|
// int32_t,
|
|
// void *);
|
|
// typedef void (*fib_quality_t) (int16_t,
|
|
// void *);
|
|
// typedef void (*programQuality_t)(int16_t,
|
|
// int16_t,
|
|
// int16_t,
|
|
// void *);
|
|
//
|
|
// Not (yet?) implemented
|
|
// typedef void (*programdata_t) (audiodata_t *,
|
|
// void *);
|
|
|
|
|
|
// typedef void (*syncsignal_t) (bool, void *);
|
|
PyObject *callbackSyncSignal = NULL;
|
|
static
|
|
void callback_syncSignal (bool b, void *ctx) {
|
|
PyObject *arglist;
|
|
PyObject *result;
|
|
PyGILState_STATE gstate;
|
|
|
|
gstate = PyGILState_Ensure ();
|
|
arglist = Py_BuildValue ("(b)", b);
|
|
result = PyEval_CallObject (callbackSyncSignal, arglist);
|
|
if (arglist != NULL)
|
|
Py_DECREF (arglist);
|
|
if (result != NULL)
|
|
Py_DECREF (result);
|
|
PyGILState_Release (gstate);
|
|
}
|
|
|
|
// typedef void (*systemdata_t) (bool,
|
|
// int16_t,
|
|
// int32_t,
|
|
// void *);
|
|
PyObject *callbackSystemData = NULL;
|
|
static
|
|
void callback_systemData (bool b, int16_t snr, int32_t offs, void *ctx) {
|
|
PyObject *arglist;
|
|
PyObject *result;
|
|
PyGILState_STATE gstate;
|
|
|
|
gstate = PyGILState_Ensure ();
|
|
arglist = Py_BuildValue ("(bhi)", b, snr, offs);
|
|
result = PyEval_CallObject (callbackSystemData, arglist);
|
|
if (arglist != NULL)
|
|
Py_DECREF (arglist);
|
|
if (result != NULL)
|
|
Py_DECREF (result);
|
|
PyGILState_Release (gstate);
|
|
}
|
|
|
|
// typedef void (*fib_quality_t) (int16_t,
|
|
// void *);
|
|
PyObject *callbackFibQuality = NULL;
|
|
static
|
|
void callback_fibQuality (int16_t q, void *ctx) {
|
|
PyObject *arglist;
|
|
PyObject *result;
|
|
PyGILState_STATE gstate;
|
|
|
|
gstate = PyGILState_Ensure ();
|
|
arglist = Py_BuildValue ("(h)", q);
|
|
result = PyEval_CallObject (callbackFibQuality, arglist);
|
|
if (arglist != NULL)
|
|
Py_DECREF (arglist);
|
|
if (result)
|
|
Py_DECREF (result);
|
|
PyGILState_Release (gstate);
|
|
}
|
|
// typedef void (*programQuality_t)(int16_t,
|
|
// int16_t,
|
|
// int16_t,
|
|
// void *);
|
|
//
|
|
PyObject *callbackProgramQuality = NULL;
|
|
static
|
|
void callback_programQuality (int16_t c1,
|
|
int16_t c2, int16_t c3, void *ctx) {
|
|
PyObject *arglist;
|
|
PyObject *result;
|
|
PyGILState_STATE gstate;
|
|
|
|
gstate = PyGILState_Ensure ();
|
|
arglist = Py_BuildValue ("hhh", c1, c2, c3);
|
|
result = PyEval_CallObject (callbackProgramQuality, arglist);
|
|
if (arglist != NULL)
|
|
Py_DECREF (arglist);
|
|
if (result != NULL)
|
|
Py_DECREF (result);
|
|
PyGILState_Release (gstate);
|
|
}
|
|
|
|
static
|
|
PyObject *callbackEnsembleName = NULL;
|
|
static
|
|
void callback_ensembleName (std::string s, int32_t id, void *ctx) {
|
|
PyGILState_STATE gstate;
|
|
PyObject *arglist;
|
|
PyObject *result;
|
|
|
|
gstate = PyGILState_Ensure ();
|
|
arglist = Py_BuildValue ("(si)", s. c_str (), id);
|
|
result = PyEval_CallObject (callbackEnsembleName, arglist);
|
|
if (arglist != NULL)
|
|
Py_DECREF (arglist);
|
|
if (result != NULL)
|
|
Py_DECREF (result);
|
|
PyGILState_Release (gstate);
|
|
}
|
|
|
|
static
|
|
PyObject *callbackProgramName = NULL;
|
|
static
|
|
void callback_programName (std::string s, int32_t id, void *ctx) {
|
|
PyGILState_STATE gstate;
|
|
PyObject *arglist;
|
|
PyObject *result;
|
|
|
|
gstate = PyGILState_Ensure ();
|
|
arglist = Py_BuildValue ("(si)", s. c_str (), id);
|
|
result = PyEval_CallObject (callbackProgramName, arglist);
|
|
if (arglist != NULL)
|
|
Py_DECREF (arglist);
|
|
if (result != NULL)
|
|
Py_DECREF (result);
|
|
PyGILState_Release (gstate);
|
|
}
|
|
|
|
// typedef void (*audioOut_t)(int16_t *, int32_t, int32_t, void *);
|
|
PyObject *callbackAudioOut = NULL;
|
|
static
|
|
void callback_audioOut (int16_t *b, int32_t size, int32_t rate, void *ctx) {
|
|
PyObject *arglist;
|
|
PyObject *result;
|
|
PyObject *theArray;
|
|
PyGILState_STATE gstate;
|
|
npy_intp dims [2];
|
|
float data [size / 2][2];
|
|
int i;
|
|
|
|
dims [0] = size / 2;
|
|
dims [1] = 2;
|
|
if (size == 0)
|
|
return;
|
|
for (i = 0; i < size / 2; i ++) {
|
|
data [i] [0] = b [2 * i] / 32767.0;
|
|
data [i] [1] = b [2 * i + 1] / 32767.0;
|
|
}
|
|
gstate = PyGILState_Ensure ();
|
|
theArray = PyArray_SimpleNewFromData (2, dims,
|
|
NPY_FLOAT, (void *)data);
|
|
arglist = Py_BuildValue ("Oii", theArray, size / 2, rate);
|
|
result = PyEval_CallObject (callbackAudioOut, arglist);
|
|
if (arglist != NULL)
|
|
Py_DECREF (arglist);
|
|
if (theArray != NULL)
|
|
Py_DECREF (theArray);
|
|
if (result != NULL)
|
|
Py_DECREF (result);
|
|
PyGILState_Release (gstate);
|
|
}
|
|
|
|
// typedef void (*dataOut_t) (std::string, void *ctx);
|
|
PyObject *callbackDataOut = NULL;
|
|
static
|
|
void callback_dataOut (std::string str, void *ctx) {
|
|
PyObject *arglist;
|
|
PyObject *result;
|
|
PyGILState_STATE gstate;
|
|
|
|
gstate = PyGILState_Ensure ();
|
|
arglist = Py_BuildValue ("z", str. c_str ());
|
|
result = PyEval_CallObject (callbackDataOut, arglist);
|
|
if (arglist != NULL)
|
|
Py_DECREF (arglist);
|
|
if (result != NULL)
|
|
Py_DECREF (result);
|
|
PyGILState_Release (gstate);
|
|
(void)result;
|
|
}
|
|
|
|
// typedef void (*programdata_t) (audiodata *, void *ctx);
|
|
PyObject *callbackProgramData = NULL;
|
|
static
|
|
void callback_programdata (audiodata *d, void *ctx) {
|
|
PyObject *arglist;
|
|
PyObject *result;
|
|
PyGILState_STATE gstate;
|
|
|
|
if (!d -> defined)
|
|
return;
|
|
gstate = PyGILState_Ensure ();
|
|
arglist = Py_BuildValue ("hhhhh", d -> subchId,
|
|
d -> startAddr,
|
|
d -> length,
|
|
d -> bitRate,
|
|
d -> ASCTy);
|
|
result = PyEval_CallObject (callbackProgramData, arglist);
|
|
if (arglist != NULL)
|
|
Py_DECREF (arglist);
|
|
if (result != NULL)
|
|
Py_DECREF (result);
|
|
PyGILState_Release (gstate);
|
|
(void)result;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// Here the real API starts
|
|
//
|
|
// We do not support spectra or so.
|
|
//
|
|
// void *dabInit (deviceHandler *,
|
|
// uint8_t, // dab Mode
|
|
// RingBuffer<std::complex<float>> *spectrumBuffer,
|
|
// RingBuffer<std::complex<float>> *iqBuffer,
|
|
// syncsignal_t syncsignalHandler,
|
|
// systemdata_t systemdataHandler,
|
|
// ensemblename_t ensemblenameHandler,
|
|
// programname_t programnamehandler,
|
|
// fib_quality_t fib_qualityHandler,
|
|
// audioOut_t audioOut_Handler,
|
|
// dataOut_t dataOut_Handler,
|
|
// bytesOut_t bytesOut_Handler,
|
|
// programdata_t programdataHandler,
|
|
// programQuality_t program_qualityHandler,
|
|
// void *userData);
|
|
|
|
|
|
|
|
PyObject *dabInit_p (PyObject *self, PyObject *args) {
|
|
int theMode = 127;
|
|
char *theChannel;
|
|
int32_t frequency;
|
|
int16_t theGain;
|
|
PyObject *cbsH; // callback for syncsignalHandler
|
|
PyObject *cbsD; // callback for systemdataHandler
|
|
PyObject *cbeN; // callback for ensemble name
|
|
PyObject *cbpN; // callback for program name
|
|
PyObject *cbfQ; // callback for fib quality
|
|
PyObject *cbaO; // callback for audioOut
|
|
PyObject *cbdO; // callback for dataOut
|
|
PyObject *cbpD; // callback for program data
|
|
PyObject *cbpQ; // callback for program Quality
|
|
bandHandler dabBand;
|
|
void *result;
|
|
|
|
(void)PyArg_ParseTuple (args,
|
|
"shhOOOOOOOOO",
|
|
&theChannel,
|
|
&theGain,
|
|
&theMode,
|
|
&cbsH,
|
|
&cbsD,
|
|
&cbeN,
|
|
&cbpN,
|
|
&cbfQ,
|
|
&cbaO,
|
|
&cbdO,
|
|
&cbpD,
|
|
&cbpQ
|
|
);
|
|
//
|
|
// The callbacks should be callable
|
|
|
|
if (!PyCallable_Check (cbsH)) {
|
|
PyErr_SetString(PyExc_TypeError, "parameter for audio must be callable");
|
|
goto err;
|
|
}
|
|
|
|
if (!PyCallable_Check (cbsD)) {
|
|
PyErr_SetString(PyExc_TypeError, "parameter for data must be callable");
|
|
goto err;
|
|
}
|
|
|
|
if (!PyCallable_Check (cbeN)) {
|
|
PyErr_SetString(PyExc_TypeError, "parameter for ensemble must be callable");
|
|
goto err;
|
|
}
|
|
|
|
if (!PyCallable_Check (cbpN)) {
|
|
PyErr_SetString(PyExc_TypeError, "parameter for program data must be callable");
|
|
goto err;
|
|
}
|
|
|
|
if (!PyCallable_Check (cbfQ)) {
|
|
PyErr_SetString(PyExc_TypeError, "parameter for ensemble must be callable");
|
|
goto err;
|
|
}
|
|
|
|
if (!PyCallable_Check (cbaO)) {
|
|
PyErr_SetString(PyExc_TypeError, "parameter for program data must be callable");
|
|
goto err;
|
|
}
|
|
|
|
if (!PyCallable_Check (cbdO)) {
|
|
PyErr_SetString(PyExc_TypeError, "parameter for ensemble must be callable");
|
|
goto err;
|
|
}
|
|
|
|
if (!PyCallable_Check (cbpD)) {
|
|
PyErr_SetString(PyExc_TypeError, "parameter for ensemble must be callable");
|
|
goto err;
|
|
}
|
|
|
|
if (!PyCallable_Check (cbpQ)) {
|
|
PyErr_SetString(PyExc_TypeError, "parameter for program data must be callable");
|
|
goto err;
|
|
}
|
|
|
|
Py_XINCREF (cbsH);
|
|
if (callbackSyncSignal != NULL)
|
|
Py_XDECREF (callbackSyncSignal);
|
|
callbackSyncSignal = cbsH;
|
|
|
|
Py_XINCREF (cbsD);
|
|
if (callbackSystemData != NULL)
|
|
Py_XDECREF (callbackSystemData);
|
|
callbackSystemData = cbsD;
|
|
|
|
Py_XINCREF (cbeN);
|
|
if (callbackEnsembleName != NULL)
|
|
Py_XDECREF (callbackEnsembleName);
|
|
callbackEnsembleName = cbeN;
|
|
|
|
Py_XINCREF (cbpN);
|
|
if (callbackProgramName != NULL)
|
|
Py_XDECREF (callbackProgramName);
|
|
callbackProgramName = cbpN;
|
|
|
|
Py_XINCREF (cbfQ);
|
|
if (callbackFibQuality != NULL)
|
|
Py_XDECREF (callbackFibQuality);
|
|
callbackFibQuality = cbfQ;
|
|
|
|
Py_XINCREF (cbaO);
|
|
if (callbackAudioOut != NULL)
|
|
Py_XDECREF (callbackAudioOut);
|
|
callbackAudioOut = cbaO;
|
|
|
|
Py_XINCREF (cbdO);
|
|
if (callbackDataOut != NULL)
|
|
Py_XDECREF (callbackDataOut);
|
|
callbackDataOut = cbdO;
|
|
|
|
Py_XINCREF (cbpD);
|
|
if (callbackProgramData != NULL)
|
|
Py_XDECREF (callbackProgramData);
|
|
callbackProgramData = cbpD;
|
|
|
|
Py_XINCREF (cbpQ);
|
|
if (callbackProgramQuality != NULL)
|
|
Py_XDECREF (callbackProgramQuality);
|
|
callbackProgramQuality = cbpQ;
|
|
|
|
frequency = dabBand. Frequency (BAND_III, theChannel);
|
|
try {
|
|
#ifdef HAVE_SDRPLAY
|
|
theDevice = new sdrplayHandler (frequency,
|
|
3,
|
|
theGain,
|
|
true,
|
|
0, 0);
|
|
#elif HAVE_AIRSPY
|
|
theDevice = new airspyHandler (frequency,
|
|
0,
|
|
theGain);
|
|
#elif HAVE_RTLSDR
|
|
theDevice = new rtlsdrHandler (frequency,
|
|
0,
|
|
theGain,
|
|
false,
|
|
0);
|
|
#else
|
|
theDevice = new deviceHandler ();
|
|
#endif
|
|
|
|
}
|
|
catch (int e) {
|
|
fprintf (stdout, "allocating device failed\n");
|
|
exit (21);
|
|
}
|
|
result = dabInit (theDevice,
|
|
theMode,
|
|
NULL,
|
|
NULL,
|
|
(syncsignal_t) &callback_syncSignal,
|
|
(systemdata_t) &callback_systemData,
|
|
(ensemblename_t) &callback_ensembleName,
|
|
(programname_t) &callback_programName,
|
|
(fib_quality_t) &callback_fibQuality,
|
|
(audioOut_t) &callback_audioOut,
|
|
(dataOut_t) &callback_dataOut,
|
|
(bytesOut_t) NULL,
|
|
(programdata_t) &callback_programdata,
|
|
(programQuality_t) &callback_programQuality,
|
|
NULL
|
|
);
|
|
if (result == NULL) {
|
|
fprintf (stdout, "Sorry, did not work\n");
|
|
goto err;
|
|
}
|
|
return PyCapsule_New (result, "library_object", NULL);
|
|
|
|
err:
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
PyObject *dabExit_p (PyObject *self, PyObject *args) {
|
|
PyObject *handle_capsule;
|
|
void *handle;
|
|
|
|
PyArg_ParseTuple (args, "O", &handle_capsule);
|
|
handle = PyCapsule_GetPointer (handle_capsule, "library_object");
|
|
dabExit (handle);
|
|
Py_INCREF (Py_None);
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
PyObject *startProcessing_p (PyObject *self, PyObject *args) {
|
|
PyObject *handle_capsule;
|
|
void *handle;
|
|
PyObject *result;
|
|
bool b;
|
|
|
|
PyArg_ParseTuple (args, "O", &handle_capsule);
|
|
handle = PyCapsule_GetPointer (handle_capsule, "library_object");
|
|
b = theDevice -> restartReader ();
|
|
if (b) {
|
|
fprintf (stdout, "device restarted\n");
|
|
dabStartProcessing (handle);
|
|
}
|
|
else
|
|
fprintf (stdout, "restart failed\n");
|
|
result = Py_BuildValue ("(b)", b);
|
|
Py_INCREF (result);
|
|
return result;
|
|
}
|
|
|
|
PyObject *dabReset_p (PyObject *self, PyObject *args) {
|
|
PyObject *handle_capsule;
|
|
void *handle;
|
|
|
|
PyArg_ParseTuple (args, "O", &handle_capsule);
|
|
|
|
handle = PyCapsule_GetPointer (handle_capsule, "library_object");
|
|
dabReset (handle);
|
|
Py_INCREF (Py_None);
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
PyObject *dabStop_p (PyObject *self, PyObject *args) {
|
|
PyObject *handle_capsule;
|
|
void *handle;
|
|
|
|
PyArg_ParseTuple (args, "O", &handle_capsule);
|
|
handle = PyCapsule_GetPointer (handle_capsule, "library_object");
|
|
dabStop (handle);
|
|
theDevice -> stopReader ();
|
|
Py_INCREF (Py_None);
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
// bool dab_Service (std::string, void *handle);
|
|
PyObject *dabService_p (PyObject *self, PyObject *args) {
|
|
PyObject *handle_capsule;
|
|
void *handle;
|
|
char *s;
|
|
std::string ss;
|
|
|
|
PyArg_ParseTuple (args, "sO", &s, &handle_capsule);
|
|
fprintf (stdout, "%s zou moeten worden gestart\n", s);
|
|
ss = std::string (s);
|
|
handle = PyCapsule_GetPointer (handle_capsule, "library_object");
|
|
dabService (ss, handle);
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
static PyMethodDef module_methods [] = {
|
|
{"dabInit", dabInit_p, METH_VARARGS, ""},
|
|
{"dabExit", dabExit_p, METH_VARARGS, ""},
|
|
{"dabReset", dabReset_p, METH_VARARGS, ""},
|
|
{"dabProcessing", startProcessing_p, METH_VARARGS, ""},
|
|
{"dabStop", dabStop_p, METH_VARARGS, ""},
|
|
{"dabService", dabService_p, METH_VARARGS, ""},
|
|
{NULL, NULL, 0, NULL}
|
|
};
|
|
|
|
static struct PyModuleDef dab_lib = {
|
|
PyModuleDef_HEAD_INIT,
|
|
"dab_lib",
|
|
"",
|
|
-1,
|
|
module_methods,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
PyObject *PyInit_libdab_lib (void) {
|
|
if (!PyEval_ThreadsInitialized ())
|
|
PyEval_InitThreads ();
|
|
import_array ();
|
|
return PyModule_Create (&dab_lib);
|
|
}
|
|
|