First part of signal generator implementation. White noise and sine waves are working.

This commit is contained in:
Anne de Jong 2019-12-28 16:58:13 +01:00
parent 3a86facb5a
commit aa3581cf74
7 changed files with 255 additions and 87 deletions

View File

@ -14,6 +14,7 @@ add_library(lasp_lib
lasp_aps.c
lasp_ps.c
lasp_mq.c
lasp_siggen.c
lasp_worker.c
lasp_dfifo.c
lasp_filterbank.c

View File

@ -30,6 +30,8 @@ void DBG_AssertFailedExtImplementation(const char* file,
DBG_AssertFailedExtImplementation(__FILE__, __LINE__, assert_string ); \
}
#define assertvalidptr(ptr) dbgassert(ptr,NULLPTRDEREF)
#else // LASP_DEBUG not defined
#define dbgassert(assertion, assert_string)

141
lasp/c/lasp_siggen.c Normal file
View File

@ -0,0 +1,141 @@
// lasp_siggen.c
//
// Author: J.A. de Jong -ASCEE
//
// Description:
// Signal generator implementation
//////////////////////////////////////////////////////////////////////
#define TRACERPLUS (-5)
#include "lasp_alloc.h"
#include "lasp_assert.h"
#include "lasp_mat.h"
#define PRIVATE_SIZE 32
typedef enum {
SINEWAVE = 0,
WHITENOISE,
PINKNOISE,
SWEEP,
} SignalType;
typedef struct {
SignalType signaltype;
d fs; // Sampling frequency [Hz]
void* private;
char private_data[PRIVATE_SIZE];
} Siggen;
typedef struct {
d curtime;
d omg;
} SinewaveSpecific;
Siggen* Siggen_Sinewave_create(const d fs, const d freq) {
fsTRACE(15);
Siggen* sinesiggen = a_malloc(sizeof(Siggen));
sinesiggen->signaltype = SINEWAVE;
sinesiggen->fs = fs;
sinesiggen->private = sinesiggen->private_data;
SinewaveSpecific* sp = (SinewaveSpecific*) sinesiggen->private;
sp->curtime = 0;
sp->omg = 2*number_pi*freq;
feTRACE(15);
return sinesiggen;
}
Siggen* Siggen_Whitenoise_create() {
fsTRACE(15);
Siggen* whitenoise = a_malloc(sizeof(Siggen));
whitenoise->signaltype = WHITENOISE;
feTRACE(15);
return whitenoise;
}
void Siggen_free(Siggen* siggen) {
fsTRACE(15);
assertvalidptr(siggen);
if(siggen->signaltype == SWEEP) {
/* Sweep specific stuff here */
}
a_free(siggen);
feTRACE(15);
}
static void Sinewave_genSignal(Siggen* siggen, SinewaveSpecific* sine, vd* samples) {
fsTRACE(15);
assertvalidptr(sine);
d ts = 1/siggen->fs;
d omg = sine->omg;
d curtime = sine->curtime;
for(us i =0; i< samples->n_rows; i++) {
setvecval(samples, i, sin(omg*curtime));
curtime = curtime + ts;
}
sine->curtime = curtime;
feTRACE(15);
}
static d gaussrand() {
static d V1, V2, S;
static int phase = 0;
d X;
if(phase == 0) {
do {
d U1 = (d)rand() / RAND_MAX;
d U2 = (d)rand() / RAND_MAX;
V1 = 2 * U1 - 1;
V2 = 2 * U2 - 1;
S = V1 * V1 + V2 * V2;
} while(S >= 1 || S == 0);
X = V1 * sqrt(-2 * log(S) / S);
} else
X = V2 * sqrt(-2 * log(S) / S);
phase = 1 - phase;
return X;
}
static void Whitenoise_genSignal(Siggen* siggen, vd* samples) {
for(us i =0; i< samples->n_rows; i++) {
d rn = gaussrand();
setvecval(samples, i, rn);
}
}
void Siggen_genSignal(Siggen* siggen,vd* samples) {
fsTRACE(15);
assertvalidptr(siggen);
assert_vx(samples);
d fs = siggen->fs;
switch(siggen->signaltype) {
case SINEWAVE:
Sinewave_genSignal(siggen, (SinewaveSpecific*) siggen->private,
samples);
break;
case WHITENOISE:
Whitenoise_genSignal(siggen, samples);
}
feTRACE(15);
}
//////////////////////////////////////////////////////////////////////

57
lasp/c/lasp_siggen.h Normal file
View File

@ -0,0 +1,57 @@
// lasp_siggen.h
//
// Author: J.A. de Jong - ASCEE
//
// Description:
// Header file for signal generation routines
//
//////////////////////////////////////////////////////////////////////
#pragma once
#ifndef LASP_SIGGEN_H
#define LASP_SIGGEN_H
#include "lasp_mat.h"
typedef struct {} Siggen;
/**
* Create a sine wave signal generator
*
* @param[in] fs: Sampling frequency [Hz]
* @param[freq] Sine wave frequency [Hz]
*/
Siggen* Siggen_Sinewave_create(const d fs,const d freq);
/**
* Create a white noise signal generator
*
* @return Siggen* handle
*/
Siggen* Siggen_Whitenoise_create();
/**
* Create a pink (1/f) noise signal generator
*
* @param[in] fs: Sampling frequency [Hz]
* @return Siggen* handle
*/
Siggen* Siggen_Pinknoise_create(const us fs);
/* Siggen* Siggen_ForwardSweep_create(const d fs,; */
/* Siggen* Siggen_(const d fs,; */
/**
* Obtain a new piece of signal
*
* @param[in] Siggen* Signal generator private data
* @param[out] samples Samples to fill. Vector should be pre-allocated
*/
void Siggen_genSignal(Siggen*,vd* samples);
/**
* Free Siggen data
*
* @param[in] Siggen* Signal generator private data
*/
void Siggen_free(Siggen*);
#endif LASP_SIGGEN_H
//////////////////////////////////////////////////////////////////////

View File

@ -65,6 +65,8 @@ class AvStream:
rtaudio_inputparams = None
rtaudio_outputparams = None
self.nframes_per_block = 2048
if self.duplex_mode or avtype == AvType.audio_output:
rtaudio_outputparams = {'deviceid': device.index,
# TODO: Add option to specify the number of output channels to use
@ -92,7 +94,7 @@ class AvStream:
rtaudio_inputparams, # Inputparams
self.sampleformat, # Sampleformat
self.samplerate,
2048, # Buffer size in frames
self.nframes_per_block, # Buffer size in frames
self._audioCallback)
except Exception as e:

View File

@ -1,86 +0,0 @@
#!/usr/bin/env python3.6
# -*- coding: utf-8 -*-
"""
Description: Read data from image stream and record sound at the same time
"""
from .lasp_atomic import Atomic
from threading import Thread, Condition, Lock
from .lasp_avstream import AvType, AvStream
import numpy as np
class SignalGenerator:
"""
Base class for all signal generator types
"""
def __init__(self, stream):
if not (stream.avtype == AvType.audio_output or (stream.avtype ==
AvType.audio_input and
stream.duplex_mode)):
raise RuntimeError('Invalid stream type. Does not support audio'
'output')
self.stream = stream
self._samplerate = stream.samplerate
stream.addCallback(self.streamCallback, AvType.audio_output)
def stop(self):
"""
Stop generating the signal
"""
self.stream.removeCallback(self.streamCallback, AvType.audio_output)
def streamCallback(self, indata, outdata, blockctr):
"""
Callback from AvStream.
"""
signal = self.getSignal(blockctr*outdata.shape[0], outdata.shape[0])
dtype = self.stream.numpy_dtype
if dtype == np.float32 or dtype == np.float64:
fac = 1
else:
bitdepth_fixed = self.stream.sampwidth*8
fac = 2**(bitdepth_fixed-1)
outdata[:, :] = (fac*signal).astype(dtype)[:, np.newaxis]
class SineGenerator(SignalGenerator):
def __init__(self, stream, freq):
self._omg = 2*freq*np.pi
super().__init__(stream)
def getSignal(self, startidx, frames):
samplerate = self._samplerate
streamtime = startidx/samplerate
t = np.linspace(streamtime, streamtime + frames/samplerate, frames)
return 0.1*np.sin(self._omg*t)
class NoiseGenerator(SignalGenerator):
def __init__(self, stream, noisetype):
super().__init__(stream)
def getSignal(self, startidx, frames):
return 0.1*np.random.randn(frames)
class NoiseType:
white = (0, 'White noise', )
pink = (1, 'Pink noise')
types = (white, pink)
@staticmethod
def fillComboBox(combo):
for type_ in NoiseType.types:
combo.addItem(type_[1])
@staticmethod
def getCurrent(cb):
return NoiseType.types[cb.currentIndex()]

View File

@ -467,3 +467,54 @@ cdef class SPLowpass:
vd_free(&input_vd)
return result
cdef extern from "lasp_siggen.h":
ctypedef struct c_Siggen "Siggen"
c_Siggen* Siggen_Whitenoise_create()
c_Siggen* Siggen_Sinewave_create(d fs, d freq)
void Siggen_genSignal(c_Siggen*, vd* samples) nogil
void Siggen_free(c_Siggen*)
cdef class Siggen:
cdef c_Siggen *_siggen
def __cinit__(self):
self._siggen = NULL
def __dealloc__(self):
if self._siggen:
Siggen_free(self._siggen)
def genSignal(self, us nsamples):
output = np.empty(nsamples, dtype=np.float)
assert self._siggen != NULL
cdef d[:] output_view = output
cdef dmat output_dmat = dmat_foreign_data(nsamples,
1,
&output_view[0],
False)
with nogil:
Siggen_genSignal(self._siggen,
&output_dmat)
return output
@staticmethod
def sineWave(fs, freq):
cdef c_Siggen* c_siggen = Siggen_Sinewave_create(fs, freq)
siggen = Siggen()
siggen._siggen = c_siggen
return siggen
@staticmethod
def whiteNoise():
cdef c_Siggen* c_siggen = Siggen_Whitenoise_create()
siggen = Siggen()
siggen._siggen = c_siggen
return siggen