Struggle to program an LFO - c

based on some tutorial code I've found I coded a little synthesizer with three oscilators and four different waveform. It works well and I want to add an LFO to module the sounds. Since I didn't coded everything on my own I'm a bit confused of how I could fit the LFO formula on my code. This is more or less what I tried in order to implement the LFO formula on a sinewave.(This formula is something like this: sinewaveFormula + 0.5 * Sinefreq * sin(2pi*1) * time)
double normalize(double phase)
{
double cycles = phase/(2.0*pi);
phase -= trunc(cycles) * 2.0 * pi;
if (phase < 0) phase += 2.0*pi;
return phase;
}
double sine(double phase)
{ phase = normalize(phase); return (sin(phase));}
static void build_sine_table(int16_t *data, int wave_length) {
double phase_increment = (2.0f * pi) / (double)wave_length;
double current_phase = 0;
for(int i = 0; i < wave_length; i++) {
int sample = synthOsc(current_phase, oscNum, selectedWave, selectedWave2, selectedWave3, intensity, intensity2, intensity3) + 0.5 * ((current_phase* wave_length) / (2*pi)) * sin(2*pi*(1.0)) * wave_length;
data[i] = (int16_t)sample;
current_phase += phase_increment;
}
}
static void write_samples(int16_t *s_byteStream, long begin, long end, long length) {
if(note > 0) {
double d_sample_rate = sample_rate;
double d_table_length = table_length;
double d_note = note;
// get correct phase increment for note depending on sample rate and table length.
double phase_increment = (get_pitch(d_note) / d_sample_rate) * d_table_length;
// loop through the buffer and write samples.
for (int i = 0; i < length; i+=2) {
phase_double += phase_increment;
phase_int = (int)phase_double;
if(phase_double >= table_length) {
double diff = phase_double - table_length;
phase_double = diff;
phase_int = (int)diff;
}
if(phase_int < table_length && phase_int > -1) {
if(s_byteStream != NULL) {
int16_t sample = sine_waveform_wave[phase_int];
target_amp = update_envelope();
if(smoothing_enabled) {
// move current amp towards target amp for a smoother transition.
if(current_amp < target_amp) {
current_amp += smoothing_amp_speed;
if(current_amp > target_amp) {
current_amp = target_amp;
}
} else if(current_amp > target_amp) {
current_amp -= smoothing_amp_speed;
if(current_amp < target_amp) {
current_amp = target_amp;
}
}
} else {
current_amp = target_amp;
}
sample *= current_amp; // scale volume.
s_byteStream[i+begin] = sample; // left channel
s_byteStream[i+begin+1] = sample; // right channel
}
}
}
}
}
The code compile but there's no LFO on the sine. I don't understand how I could make this formula work with this code.

It may help to get a basic understanding of how a LFO actually works. It is not that difficult - as an LFO is just another oscillator that is mixed to the waveform you want to modulate.
I would suggest to remove your LFO formular from your call of synthOsc(), then you get a clean oscillator signal again. As a next step, create another oscillator signal for which you can use a very low frequency. Mix both signals together and you are done.
Expresssed in simple math, it is like this:
int the_sample_you_want_to_modulate = synthOsc1(...);
int a_sample_with_very_low_frequency = synthOsc2(...);
Mixing two waveforms is done through addition:
int mixed_sample = the_sample_you_want_to_modulate + a_sample_with_very_low_frequency;
The resulting sample will sweep now based on the frequency you have used for synthOsc2().
As you can see, to implement an LFO you actually do not need a separate formular. You already have the formular when you know how to create an oscillator.
Note that if you add two sine oscillators that have the exact same frequency, the resulting signal will just get louder. But when each has a different frequency, you will get a new waveform. For LFOs (which are in fact just ordinary oscillators - like in your build_sine_table() function) you typically set a very low frequency: 1 - 10 Hz is low enough to get an audible sweep. For higher frequencies you get chords as a result.

Related

Does fmodf() cause a hardfault in stm32?

I am trying to create a modulated waveform out of 2 sine waves.
To do this I need the modulo(fmodf) to know what amplitude a sine with a specific frequency(lo_frequency) has at that time(t). But I get a hardfault when the following line is executed:
j = fmodf(2 * PI * lo_frequency * t, 2 * PI);
Do you have an idea why this gives me a hardfault ?
Edit 1:
I exchanged fmodf with my_fmodf:
float my_fmodf(float x, float y){
if(y == 0){
return 0;
}
float n = x / y;
return x - n * y;
}
But still the hardfault occurs, and when I debug it it doesn't even jump into this function(my_fmodf).
Heres the whole function in which this error occurs:
int* create_wave(int* message){
/* Mixes the message signal at 10kHz and the carrier at 40kHz.
* When a bit of the message is 0 the amplitude is lowered to 10%.
* When a bit of the message is 1 the amplitude is 100%.
* The output of the STM32 can't be negative, thats why the wave swings between
* 0 and 256 (8bit precision for faster DAC)
*/
static int rf_frequency = 10000;
static int lo_frequency = 40000;
static int sample_rate = 100000;
int output[sample_rate];
int index, mix;
float j, t;
for(int i = 0; i <= sample_rate; i++){
t = i * 0.00000001f; // i * 10^-8
j = my_fmodf(2 * PI * lo_frequency * t, 2 * PI);
if (j < 0){
j += (float) 2 * PI;
}
index = floor((16.0f / (lo_frequency/rf_frequency * 0.0001f)) * t);
if (index < 16) {
if (!message[index]) {
mix = 115 + sin1(j) * 0.1f;
} else {
mix = sin1(j);
}
} else {
break;
}
output[i] = mix;
}
return output;
}
Edit 2:
I fixed the warning: function returns address of local variable [-Wreturn-local-addr] the way "chux - Reinstate Monica" suggested.
int* create_wave(int* message){
static uint16_t rf_frequency = 10000;
static uint32_t lo_frequency = 40000;
static uint32_t sample_rate = 100000;
int *output = malloc(sizeof *output * sample_rate);
uint8_t index, mix;
float j, n, t;
for(int i = 0; i < sample_rate; i++){
t = i * 0.00000001f; // i * 10^-8
j = fmodf(2 * PI * lo_frequency * t, 2 * PI);
if (j < 0){
j += 2 * PI;
}
index = floor((16.0f / (lo_frequency/rf_frequency * 0.0001f)) * t);
if (index < 16) {
if (!message[index]) {
mix = (uint8_t) floor(115 + sin1(j) * 0.1f);
} else {
mix = sin1(j);
}
} else {
break;
}
output[i] = mix;
}
return output;
}
But now I get the hardfault on this line:
output[i] = mix;
EDIT 3:
Because the previous code contained a very large buffer array that did not fit into the 16KB SRAM of the STM32F303K8 I needed to change it.
Now I use a "ping-pong" buffer where I use the callback of the DMA for "first-half-transmitted" and "completly-transmitted":
void HAL_DAC_ConvHalfCpltCallbackCh1(DAC_HandleTypeDef * hdac){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);
for(uint16_t i = 0; i < 128; i++){
new_value = sin_table[(i * 8) % 256];
if (message[message_index] == 0x0){
dac_buf[i] = new_value * 0.1f + 115;
} else {
dac_buf[i] = new_value;
}
}
}
void HAL_DAC_ConvCpltCallbackCh1 (DAC_HandleTypeDef * hdac){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);
for(uint16_t i = 128; i < 256; i++){
new_value = sin_table[(i * 8) % 256];
if (message[message_index] == 0x0){
dac_buf[i] = new_value * 0.1f + 115;
} else {
dac_buf[i] = new_value;
}
}
message_index++;
if (message_index >= 16) {
message_index = 0;
// HAL_DAC_Stop_DMA (&hdac1, DAC_CHANNEL_1);
}
}
And it works the way I wanted:
But the frequency of the created sine is too low.
I cap at around 20kHz but I'd need 40kHz.
I allready increased the clock by a factor of 8 so that one is maxed out:
.
I can still decrease the counter period (it is 50 at the moment), but when I do so the interrupt callback seems to take longer than the period to the next one.
At least it seems so as the output becomes very distorted when I do that.
I also tried to decrease the precision by taking only every 8th sine value but
I cant do this any more because then the output does not look like a sine wave anymore.
Any ideas how I could optimize the callback so that it takes less time ?
Any other ideas ?
Does fmodf() cause a hardfault in stm32?
It is other code problems causing the hard fault here.
Failing to compile with ample warnings
Best code tip: enable all warnings. #KamilCuk
Faster feedback than Stackoverflow.
I'd expect something like below on a well enabled compiler.
return output;
warning: function returns address of local variable [-Wreturn-local-addr]
Returning a local Object
Cannot return a local array. Allocate instead.
// int output[sample_rate];
int *output = malloc(sizeof *output * sample_rate);
return output;
Calling code will need to free() the pointer.
Out of range array access
static int sample_rate = 100000;
int output[sample_rate];
// for(int i = 0; i <= sample_rate; i++){
for(int i = 0; i < sample_rate; i++){
...
output[i] = mix;
}
Stack overflow?
static int sample_rate = 100000; int output[sample_rate]; is a large local variable. Maybe allocate or try something smaller?
Advanced: loss of precision
A good fmodf() does not lose precision. For a more precise answer consider double math for the intermediate results. An even better approach is more involved.
float my_fmodf(float x, float y){
if(y == 0){
return 0;
}
double n = 1.0 * x / y;
return (float) (x - n * y);
}
Can I not use any function within another ?
Yes. Code has other issues.
1 value every 10uS makes only 100kSPS whis is not too much for this macro. In my designs I generate > 5MSPS signals without any problems. Usually I have one buffer and DMA in circular mode. First I fill the buffer and start generation. When the half transmition DMA interrupt is trigerred I fill the first half of the buffer with fresh data. The the transmition complete interrupt is trigerred I fill the second half and this process repeats all over again.

Writing a wave generator with SDL

I've coded a simple sequencer in C with SDL 1.2 and SDL_mixer(to play .wav file). It works well and I want to add some audio synthesis to this program. I've look up the and I found this sinewave code using SDL2(https://github.com/lundstroem/synth-samples-sdl2/blob/master/src/synth_samples_sdl2_2.c)
Here's how the sinewave is coded in the program:
static void build_sine_table(int16_t *data, int wave_length)
{
/*
Build sine table to use as oscillator:
Generate a 16bit signed integer sinewave table with 1024 samples.
This table will be used to produce the notes.
Different notes will be created by stepping through
the table at different intervals (phase).
*/
double phase_increment = (2.0f * pi) / (double)wave_length;
double current_phase = 0;
for(int i = 0; i < wave_length; i++) {
int sample = (int)(sin(current_phase) * INT16_MAX);
data[i] = (int16_t)sample;
current_phase += phase_increment;
}
}
static double get_pitch(double note) {
/*
Calculate pitch from note value.
offset note by 57 halfnotes to get correct pitch from the range we have chosen for the notes.
*/
double p = pow(chromatic_ratio, note - 57);
p *= 440;
return p;
}
static void audio_callback(void *unused, Uint8 *byte_stream, int byte_stream_length) {
/*
This function is called whenever the audio buffer needs to be filled to allow
for a continuous stream of audio.
Write samples to byteStream according to byteStreamLength.
The audio buffer is interleaved, meaning that both left and right channels exist in the same
buffer.
*/
// zero the buffer
memset(byte_stream, 0, byte_stream_length);
if(quit) {
return;
}
// cast buffer as 16bit signed int.
Sint16 *s_byte_stream = (Sint16*)byte_stream;
// buffer is interleaved, so get the length of 1 channel.
int remain = byte_stream_length / 2;
// split the rendering up in chunks to make it buffersize agnostic.
long chunk_size = 64;
int iterations = remain/chunk_size;
for(long i = 0; i < iterations; i++) {
long begin = i*chunk_size;
long end = (i*chunk_size) + chunk_size;
write_samples(s_byte_stream, begin, end, chunk_size);
}
}
static void write_samples(int16_t *s_byteStream, long begin, long end, long length) {
if(note > 0) {
double d_sample_rate = sample_rate;
double d_table_length = table_length;
double d_note = note;
/*
get correct phase increment for note depending on sample rate and table length.
*/
double phase_increment = (get_pitch(d_note) / d_sample_rate) * d_table_length;
/*
loop through the buffer and write samples.
*/
for (int i = 0; i < length; i+=2) {
phase_double += phase_increment;
phase_int = (int)phase_double;
if(phase_double >= table_length) {
double diff = phase_double - table_length;
phase_double = diff;
phase_int = (int)diff;
}
if(phase_int < table_length && phase_int > -1) {
if(s_byteStream != NULL) {
int16_t sample = sine_wave_table[phase_int];
sample *= 0.6; // scale volume.
s_byteStream[i+begin] = sample; // left channel
s_byteStream[i+begin+1] = sample; // right channel
}
}
}
}
}
I don't understand how I could change the sinewave formula to genrate other waveform like square/triangle/saw ect...
EDIT:
Because I forgot to explain it, here's what I tried.
I followed the example I've seen on this video series(https://www.youtube.com/watch?v=tgamhuQnOkM). The source code of the method provided by the video is on github, and the wave generation code is looking like this:
double w(double dHertz)
{
return dHertz * 2.0 * PI;
}
// General purpose oscillator
double osc(double dHertz, double dTime, int nType = OSC_SINE)
{
switch (nType)
{
case OSC_SINE: // Sine wave bewteen -1 and +1
return sin(w(dHertz) * dTime);
case OSC_SQUARE: // Square wave between -1 and +1
return sin(w(dHertz) * dTime) > 0 ? 1.0 : -1.0;
case OSC_TRIANGLE: // Triangle wave between -1 and +1
return asin(sin(w(dHertz) * dTime)) * (2.0 / PI);
}
Because the C++ code here uses windows soun api I could not copy/paste this method to make it work on the piece of code I've found using SDL2.
So I tried to this in order to obtain a square wave:
static void build_sine_table(int16_t *data, int wave_length)
{
double phase_increment = ((2.0f * pi) / (double)wave_length) > 0 ? 1.0 : -1.0;
double current_phase = 0;
for(int i = 0; i < wave_length; i++) {
int sample = (int)(sin(current_phase) * INT16_MAX);
data[i] = (int16_t)sample;
current_phase += phase_increment;
}
}
This didn't gave me a square wave but more a saw wave.
Here's what I tried to get a triangle wave:
static void build_sine_table(int16_t *data, int wave_length)
{
double phase_increment = (2.0f * pi) / (double)wave_length;
double current_phase = 0;
for(int i = 0; i < wave_length; i++) {
int sample = (int)(asin(sin(current_phase) * INT16_MAX)) * (2 / pi);
data[i] = (int16_t)sample;
current_phase += phase_increment;
}
}
This also gave me another type of waveform, not triangle.
You’d replace the sin function call with call to one of the following:
// this is a helper function only
double normalize(double phase)
{
double cycles = phase/(2.0*M_PI);
phase -= trunc(cycles) * 2.0 * M_PI;
if (phase < 0) phase += 2.0*M_PI;
return phase;
}
double square(double phase)
{ return (normalize(phase) < M_PI) ? 1.0 : -1.0; }
double sawtooth(double phase)
{ return -1.0 + normalize(phase) / M_PI; }
double triangle(double phase)
{
phase = normalize(phase);
if (phase >= M_PI)
phase = 2*M_PI - phase;
return -1.0 + 2.0 * phase / M_PI;
}
You’d be building tables just like you did for the sine, except they’d be the square, sawtooth and triangle tables, respectively.

How to use step size to find the tone frequency in sine table? STM32

I'm trying to use the Sine Table lookup method to find the tone frequency at different step size, but when I'm converting the floating point to integer and use the oscicopte to view the frequncy, it can't display any things on screen.
Does anyone know what's the solution for this issues. Any help is apperaite.
Below is the code:
// use the formula: StepSize = 360/(Fs/f) Where Fs is the Sample frequency 44.1 kHz and f is the tone frequency.
// example: StepSize = 360/(44100/440) = 3.576, since the STM32 doesn't support the floating point, therefore, we have to use the fixed-point format which multiply it by 1000 to be 3575
int StepSize = 3575;
unsigned int v=0;
signed int sine_table[91] = {
0x800,0x823,0x847,0x86b,
0x88e,0x8b2,0x8d6,0x8f9,
0x91d,0x940,0x963,0x986,
0x9a9,0x9cc,0x9ef,0xa12,
0xa34,0xa56,0xa78,0xa9a,
0xabc,0xadd,0xaff,0xb20,
0xb40,0xb61,0xb81,0xba1,
0xbc1,0xbe0,0xc00,0xc1e,
0xc3d,0xc5b,0xc79,0xc96,
0xcb3,0xcd0,0xcec,0xd08,
0xd24,0xd3f,0xd5a,0xd74,
0xd8e,0xda8,0xdc1,0xdd9,
0xdf1,0xe09,0xe20,0xe37,
0xe4d,0xe63,0xe78,0xe8d,
0xea1,0xeb5,0xec8,0xedb,
0xeed,0xeff,0xf10,0xf20,
0xf30,0xf40,0xf4e,0xf5d,
0xf6a,0xf77,0xf84,0xf90,
0xf9b,0xfa6,0xfb0,0xfba,
0xfc3,0xfcb,0xfd3,0xfda,
0xfe0,0xfe6,0xfec,0xff0,
0xff4,0xff8,0xffb,0xffd,
0xffe,0xfff,0xfff};
unsigned int sin(int x){
x = x % 360;
if(x <= 90)
return sine_table[x];
else if ( x <= 180){
return sine_table[180 - x];
}else if ( x <= 270){
return 4096 - sine_table[x - 180];
}else{
return 4096 - sine_table[360 - x];
}
}
void main(void)
{
while(1){
v+=StepSize; // Don't know why it doesn't work in this way. not display anything on screen.
DAC->DHR12R2 = sin(v/1000); // DAC channel-2 12-bit Right aligned data
if (v >= 360) v = 0;
}
}
But, if I change the StepSize = 3; it shows the frequency:
There are a few issues with your code. But I will start with the one that you asked about.
int StepSize = 3575;
unsigned int v=0;
while(1){
v+=StepSize;
DAC->DHR12R2 = sin(v/1000);
if (v >= 360) v = 0;
}
The reason why this code doesn't work is that v is always set to 0 at the end of the loop because 3575 is greater than 360. So then you always call sin(3) because 3575/1000 is 3 in integer division.
Perhaps, you should rewrite your last line as if ((v/1000) >= 360) v = 0;. Otherwise, I would rewrite your loop like this
while(1){
v+=StepSize;
v/=1000;
DAC->DHR12R2 = sin(v);
if (v >= 360) v = 0;
}
I would also recommend that you declare your lookup table a const. So it would look like
const signed int sine_table[91] = {
Last recommendation is to choose another name for your sin function so as not to confuse with the sin library function. Even though in this case there shouldn't be a problem.

Simple compression Algorithm in C is not working..any suggestions?

I am creating a project in nordic Micro that reads a value from an Analog input termninal an output it into UART. I am now trying to compress the data using GE Proficy Historian Compression so that only changed data are outputted in the UART. But my code is not working. Outputted data are sometime still redundant.
The idea of the program is to generate an interrupt every certain amout of time, read the adc value and if its different than previous value, output it to the UART port.
the algorithm is explained here
http://www.evsystems.net/files/GE_Historian_Compression_Overview.ppt
The main portion of the code that handles the interrupt is as shown below
void ADC_IRQHandler(void)
{
/* Clear dataready event */
NRF_ADC->EVENTS_END = 0;
// write ADC value to UART port
// Compression Algorithm should occur here
uint8_t current_Val= NRF_ADC->RESULT;
//simple_uart_put(current_evaluation);
// Construct error bands around the held point
float U_tolerance = current_Val + error_tolerance;
float L_tolerance = current_Val - error_tolerance; // integer type is selected since lower tolerance could be negative
if( first_Run == false)
{
float slope = ((current_Val - Archived_Val) / (held_Time - Archived_Time)) ;
if (slope > U_Slope || slope > L_Slope)
{
Archived_Val = current_Val; // update the archived value
held_Time = 1; // reset held time
simple_uart_put(current_Val);
first_Run = true;
Archived_Val = current_Val;
}
else
{
float Upper_Slope = (U_tolerance - Archived_Val) /( held_Time - Archived_Time);
float Lower_Slope = (L_tolerance - Archived_Val)/(held_Time- Archived_Time);
if(Upper_Slope < U_Slope) // lowest upper slope is always taken as a blanket boundry
{
U_Slope = Upper_Slope;
}
if(Lower_Slope < L_Slope)
{
L_Slope = Lower_Slope;
}
held_Time += time_increment;
}
}
if (first_Run == true) // first held point always outputted
{
// calculate the slopes of the two lines
float Upper_Slope = (U_tolerance - Start_Up_Val) /( held_Time - Archived_Time);
float Lower_Slope = (L_tolerance - Start_Up_Val)/(held_Time- Archived_Time);
// Update Max and Min slopes
if(Upper_Slope < U_Slope) // lowest upper slope is always taken as a blanket boundry
{
U_Slope = Upper_Slope;
}
if(Lower_Slope < L_Slope)
{
L_Slope = Lower_Slope;
}
held_Time += time_increment;
first_Run = false;
Archived_Val = current_Val;
}
}
The veriables are defined as follow
> uint32_t error_tolerance = 50; // error tolerance value for swining door algorithm
uint8_t Start_Up_Val = 100;
float held_Time = 1;
int Archived_Time = 0;
float U_Slope = 2500;
float L_Slope = 0;
//float slope;
uint8_t Archived_Val;
bool GE_Comp(uint8_t, uint8_t, uint8_t, int);
bool first_Run = true;
float time_increment = 0.1;
Thank you all for your contribution and mostly to #Weather Vane . It was exactly as you and others suggested, the interrupt handler was executing too much coding which prohibited it from proper functionality. I now fixed the problem by diverging the parts of the code to the main function as suggested.
Regareds

Arduino Due C Timer Coding (Interrupt Counting)

I have an Arduino Due that I'm doing a lot of really neat stuff with in one section of code I'm handeling a flowsensor and getting readings from it. the flowsensor is hooked up to an interrupt as it is a hallsensor device. The problem I'm having is that my math is based off of a single seconds worth of collection data, when I turn on the interrupt and sleep for a second do the calculations for flowrate in gp/m it works great. For My purposes however I can't afford to pause and collect data, so instead I calculate a change in time of greater than a thousand milliseconds. then I take the amount of time passed since the last loop, devide it by 1000 milliseconds or 1 second then I have the percentage of 1 second stored in a variable, I multiply the count by the percentage and should arrive at the number of counts per second. This however results in an ever growing number regardless of the fact that I zero the numbers prior to the next loop. Below I've included the way I formatted the expressions and the loop code for the flowsensor, I'd post the whole code but I don't want to waste space on stackoverflow and I know that portion of code already works with the sleep method.
long currentMillis = 0;
long lastMillis = 0;
int checkMillis = 0;
volatile int NbTopsFan; //measuring the rising edges of the signal
int newNbTopsFan;
float realNbTopsFan;
float realMillis;
float Calc;
float Calcd;
int Calcf;
int hallsensor = 7;
void loop()
{
if (lastMillis = 0) {
lastMillis = millis();
}
currentMillis = millis();
checkMillis = (currentMillis - lastMillis);
if (checkMillis >= 1000) {
realMillis = (checkMillis / 1000);
realNbTopsFan = (NbTopsFan * realMillis);
newNbTopsFan = realNbTopsFan;
Calc = (newNbTopsFan * 60 / 7.5); //(Pulse frequency x 60) / 7.5Q, = flow rate
Calc = (Calc / 60);
Calc = (Calc / 3.78541);
Calc = (Calc * 1.47);
Calcd = (Calc * 100);
Calcf = Calcd;
NbTopsFan = 0;
lastMillis = 0;
checkMillis = 0;
realMillis = 0;
realNbTopsFan = 0;
newNbTopsFan = 0;
}
}
Your first line should read:
if ( lastMillis == 0 ) {
lastMillis = millis();
}
You need the double = to evaluate the variable

Resources