Files

375 lines
10 KiB
C

#include <Python.h>
#ifdef MS_WINDOWS
#include <windows.h>
#else
#include <stdlib.h>
#include <sys/time.h>
#endif
#include <complex.h>
#include <string.h>
#include "quisk.h"
// Access to config file attributes.
// NOTE: These must be called only from the main (GUI) thread,
// not from the sound thread.
int QuiskGetConfigInt(const char * name, int deflt)
{ // return deflt for failure. Accept int or float.
int res;
PyObject * attr;
if (!quisk_pyConfig || PyErr_Occurred()) {
return deflt;
}
attr = PyObject_GetAttrString(quisk_pyConfig, name);
if (attr) {
res = (int)PyInt_AsUnsignedLongMask(attr); // This works for floats too!
Py_DECREF(attr);
return res; // success
}
else {
PyErr_Clear();
}
return deflt; // failure
}
int QuiskGetConfigBoolean(const char * name, int deflt) // UNTESTED
{ // Return 1 for True, 0 for False. Return deflt for failure.
int res;
PyObject * attr;
if (!quisk_pyConfig || PyErr_Occurred()) {
return deflt;
}
attr = PyObject_GetAttrString(quisk_pyConfig, name);
if (attr) {
res = PyObject_IsTrue(attr);
Py_DECREF(attr);
return res; // success
}
else {
PyErr_Clear();
}
return deflt; // failure
}
double QuiskGetConfigDouble(const char * name, double deflt)
{ // return deflt for failure. Accept int or float.
double res;
PyObject * attr;
if (!quisk_pyConfig || PyErr_Occurred())
return deflt;
attr = PyObject_GetAttrString(quisk_pyConfig, name);
if (attr) {
res = PyFloat_AsDouble(attr);
Py_DECREF(attr);
return res; // success
}
else {
PyErr_Clear();
}
return deflt; // failure
}
char * QuiskGetConfigString(const char * name, char * deflt)
{ // Return the UTF-8 configuration string. Return deflt for failure.
Py_ssize_t size;
if (!quisk_pyConfig || PyErr_Occurred())
return deflt; // failure
PyObject * attr = PyObject_GetAttrString(quisk_pyConfig, name);
if ( ! attr) {
PyErr_Clear();
return deflt; // failure
}
if ( ! PyUnicode_Check(attr)) {
Py_DECREF(attr);
return deflt; // failure
}
const char * utf8_str = PyUnicode_AsUTF8AndSize(attr, &size);
if ( ! utf8_str) {
Py_DECREF(attr);
PyErr_Clear();
return deflt; // failure
}
Py_DECREF(attr);
return (char *)utf8_str;
}
double QuiskTimeSec(void)
{ // return time in seconds as a double
#ifdef MS_WINDOWS
FILETIME ft;
ULARGE_INTEGER ll;
GetSystemTimeAsFileTime(&ft);
ll.LowPart = ft.dwLowDateTime;
ll.HighPart = ft.dwHighDateTime;
return (double)ll.QuadPart * 1.e-7;
#else
struct timeval tv;
gettimeofday(&tv, NULL);
return (double)tv.tv_sec + tv.tv_usec * 1e-6;
#endif
}
double QuiskDeltaSec(int timer)
{ // return the number of seconds since the last call for the timer.
// There are two timers. The "timer" is either 0 or 1. Call first and throw away the result.
static double time0[2] = {0, 0};
double now; // in seconds
double delta;
#ifdef MS_WINDOWS
// Code contributed by Ben Cahill
static double timer_rate = 0;
LARGE_INTEGER L;
if (timer_rate == 0) {
if (QueryPerformanceFrequency(&L))
timer_rate = (double)L.QuadPart;
else
timer_rate = 1.0;
}
if (QueryPerformanceCounter(&L))
now = (double)L.QuadPart / timer_rate;
else
now = 0;
#else
struct timespec ts;
#ifdef CLOCK_MONOTONIC_RAW
if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) != 0)
#else
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
#endif
return 0;
now = (double)ts.tv_sec + ts.tv_nsec * 1E-9;
#endif
if (timer < 0 || timer >= 2)
return 0;
if (now < time0[timer])
now = time0[timer] = 0;
delta = now - time0[timer];
time0[timer] = now;
return delta;
}
void QuiskPrintTime(const char * str, int index)
{ // print the time and a message and the delta time for index 0 to 9
double tm;
int i;
static double time0 = 0;
static double start_time[10];
#ifdef MS_WINDOWS
static long long timer_rate = 0;
LARGE_INTEGER L;
if ( ! timer_rate) {
if (QueryPerformanceFrequency(&L))
timer_rate = L.QuadPart;
else
timer_rate = 1;
}
if (QueryPerformanceCounter(&L))
tm = (double)L.QuadPart / timer_rate;
else
tm = 0;
#else
struct timeval tv;
gettimeofday(&tv, NULL);
tm = (double)tv.tv_sec + tv.tv_usec * 1e-6;
#endif
if (index < -9 || index > 9) // error
return;
if (index < 0) {
start_time[ - index] = tm;
return;
}
if ( ! str) { // initialize
time0 = tm;
for (i = 0; i < 10; i++)
start_time[i] = tm;
return;
}
// print the time since startup, and the time since the last call
if (index > 0) {
if (str[0]) // print message and a newline
QuiskPrintf ("%12.6lf %9.3lf %9.3lf %s\n",
tm - time0, (tm - start_time[0])*1e3, (tm - start_time[index])*1e3, str);
else // no message; omit newline
QuiskPrintf ("%12.6lf %9.3lf %9.3lf ",
tm - time0, (tm - start_time[0])*1e3, (tm - start_time[index])*1e3);
}
else {
if (str[0]) // print message and a newline
QuiskPrintf ("%12.6lf %9.3lf %s\n",
tm - time0, (tm - start_time[0])*1e3, str);
else // no message; omit newline
QuiskPrintf ("%12.6lf %9.3lf ",
tm - time0, (tm - start_time[0])*1e3);
}
start_time[0] = tm;
}
void QuiskSleepMicrosec(int usec)
{
#ifdef MS_WINDOWS
int msec = (usec + 500) / 1000; // convert to milliseconds
if (msec < 1)
msec = 1;
Sleep(msec);
#else
struct timespec tspec;
tspec.tv_sec = usec / 1000000;
tspec.tv_nsec = (usec - tspec.tv_sec * 1000000) * 1000;
nanosleep(&tspec, NULL);
#endif
}
void QuiskMeasureRate(const char * msg, int count, int index, int reset)
{ //measure the sample rate for index 0 to 9. If reset, reset the count and time at each print.
double tm;
static unsigned long total[10] = {0,0,0,0,0,0,0,0,0,0};
static double time0[10] = {0,0,0,0,0,0,0,0,0,0};
static double time_pr[10] = {0,0,0,0,0,0,0,0,0,0};
if ( ! msg) { // init
time0[index] = 0;
total[index] = 0;
return;
}
if (count && time0[index] == 0) { // init
time0[index] = time_pr[index] = QuiskTimeSec();
total[index] = 0;
return;
}
if (time0[index] == 0)
return;
total[index] += count;
tm = QuiskTimeSec();
if (tm > time_pr[index] + 10.0) { // time to print
time_pr[index] = tm;
QuiskPrintf("%s count %ld, time %.3lf, rate %.3lf\n", msg, total[index], tm - time0[index], total[index] / (tm - time0[index]));
if (reset) {
total[index] = 0;
time0[index] = tm;
}
}
}
char * strMcpy(char * pDest, const char * pSrc, size_t sizeDest)
{ // replacement for strncpy()
size_t sizeCopy;
sizeCopy = strnlen(pSrc, sizeDest - 1);
memcpy(pDest, pSrc, sizeCopy);
pDest[sizeCopy] = 0;
return pDest;
}
#ifdef MS_WINDOWS
#define QP_BUF_DELTA 256
PyObject * QuiskPrintf(char * format, ...)
{ // thread safe version of printf() needed by Windows
int length;
PyObject * py_string;
static int buf_size; // size of buffer
static int buf_strlen; // number of chars in buffer
static char * buffer = NULL;
va_list args;
EnterCriticalSection(&QuiskCriticalSection);
if (buffer == NULL) { // initialize
buf_strlen = 0;
buf_size = QP_BUF_DELTA * 2;
buffer = malloc(buf_size);
}
if (format == NULL) { // return the string
py_string = PyUnicode_DecodeUTF8(buffer, buf_strlen, "replace");
buf_strlen = 0;
LeaveCriticalSection(&QuiskCriticalSection);
return py_string;
}
if (buf_size - buf_strlen < QP_BUF_DELTA * 2) { // max addition is QP_BUF_DELTA
buf_size += QP_BUF_DELTA * 2;
buffer = realloc(buffer, buf_size);
}
va_start(args, format);
length = vsnprintf(buffer + buf_strlen, QP_BUF_DELTA, format, args);
if (length < 0) {
strcpy(buffer + buf_strlen, "Encoding error\n");
buf_strlen = strlen(buffer);
}
else if (length > QP_BUF_DELTA - 1) {
buf_strlen = strlen(buffer);
}
else {
//printf("%s", buffer + buf_strlen);
buf_strlen += length;
}
va_end(args);
LeaveCriticalSection(&QuiskCriticalSection);
return NULL;
}
#endif
#define MAX_CHANNELS 8
int CircularBuffer(int channel, complex double * cSamples, int nRead, int nWrite)
{
// Initialize with cSamples NULL and nRead equal to the buffer delay in samples.
// Read with nRead, write with nWrite. Only one can be non-zero for read/write. If both are zero, return nBuf.
// Return the number of samples read or written.
static struct _channel {
complex double * cBuf; // circular sample buffer; save samples until the buffer fills to nDelay
int sizeBuf; // current size of cBuf in complex samples
int nDelay; // number of samples needed to start reading the buffer
int nBuf; // current number of complex samples in cBuf
int filling; // if true, we are filling the buffer and not returning samples
int Windex; // index of write
int Rindex; // index of read
} Channels[MAX_CHANNELS] = {0};
struct _channel * ptChannel = Channels + channel;
int i, num;
if ( ! cSamples) { // initialize
ptChannel->nDelay = nRead;
ptChannel->sizeBuf = nRead * 16;
ptChannel->cBuf = (complex double *)realloc(ptChannel->cBuf, ptChannel->sizeBuf * sizeof(complex double));
memset(ptChannel->cBuf, 0, ptChannel->sizeBuf * sizeof(complex double));
ptChannel->nBuf = 0;
ptChannel->Windex = 0;
ptChannel->Rindex = 0;
ptChannel->filling = 1;
return 0;
}
if ( ! ptChannel->cBuf)
return 0;
if (nRead == 0 && nWrite == 0)
return ptChannel->nBuf;
num = 0; // number of samples read or written
if (nWrite > 0) { // Copy samples from cSamples to cBuf
for (i = 0; i < nWrite && ptChannel->nBuf < ptChannel->sizeBuf; i++) {
ptChannel->cBuf[ptChannel->Windex++] = cSamples[i];
if (ptChannel->Windex >= ptChannel->sizeBuf)
ptChannel->Windex = 0;
ptChannel->nBuf++;
num++;
}
if (ptChannel->nBuf >= ptChannel->nDelay)
ptChannel->filling = 0;
//if (num != nWrite)
// QuiskPrintf("Circ nWrite %d %d %d %d\n", num, nWrite, ptChannel->nBuf, ptChannel->sizeBuf);
}
else if (nRead > 0) {
if ( ! ptChannel->filling) { // Copy samples from cBuf to cSamples
for (i = 0; i < nRead && ptChannel->nBuf > 0; i++) {
cSamples[i] = ptChannel->cBuf[ptChannel->Rindex++];
if (ptChannel->Rindex >= ptChannel->sizeBuf)
ptChannel->Rindex = 0;
ptChannel->nBuf--;
num++;
}
if (ptChannel->nBuf <= 0)
ptChannel->filling = 1;
}
}
return num;
}