I'm trying to tune a guitar using the mbed LPC microcontrolller.
There is a link below for an example of the project.
https://developer.mbed.org/users/adurand/notebook/guitar-tuner/
However, I am having some problems with it. Firstly, I am simulating the guitar input using an arbitrary waveform generator at the required frequency of each string. The amplifier circuit is constructed the same. I have also changed the code to utilise the Mbed LCD instead of the Nokia. I have used an oscilloscope to validate that the output going into AnalogueIn p20 of the Mbed is amplified and at the correct frequency. This is where I have problems.
Here is the code (with credit to Andrew Durand):
#include "mbed.h"
#include "adc.h"
#include "C12832.h"
#include <math.h>
#define PI 3.1415
#define SAMPLE_RATE 24000
InterruptIn button1(p12);
C12832 lcd(p5, p7, p6, p8, p11);
DigitalOut myled1(LED1);
DigitalOut myled2(LED2);
DigitalOut myled3(LED3);
//LCD and Other Random Variables
int string_select = 0;
float high, high1, in_tune, in_tune1, in_tune2, in_tune3,
low, low1, note, low_mod, high_mod;
char* key;
int Counter = 0;
int Buffer[6000];
float goertzelFilter(int samples[], float freq, int N) {
float s_prev = 0.0;
float s_prev2 = 0.0;
float coeff,normalizedfreq,power,s;
int i;
normalizedfreq = freq / SAMPLE_RATE;
coeff = 2*cos(2*PI*normalizedfreq);
for (i=0; i<N; i++) {
s = samples[i] + coeff * s_prev - s_prev2;
s_prev2 = s_prev;
s_prev = s;
}
power = s_prev2*s_prev2+s_prev*s_prev-coeff*s_prev*s_prev2;
return power;
}
ADC adc(SAMPLE_RATE, 1);
void sample_audio(int chan, uint32_t value) {
Buffer[Counter] = adc.read(p20);
Counter += 1;
}
void button1_pressed() {
string_select++;
if (string_select > 5) string_select = 0;
}
int main() {
//Interupt for Switching Strings
button1.mode(PullDown);
button1.rise(&button1_pressed);
while (1) {
switch (string_select) {
case 0:
note = 82;
key= "E2";
break;
case 1:
note = 110;
key= "A2";
break;
case 2:
note = 147;
key= "D3";
break;
case 3:
note = 196;
key= "G3";
break;
case 4:
note = 247;
key= "B3";
break;
case 5:
note = 330;
key= "E4";
break;
}
//Prepare for burst mode on all ADC pins and set up interrupt handler (using ADC library from Simon Blandford
adc.append(sample_audio);
adc.startmode(0,0);
adc.burst(1);
adc.setup(p20,1);
//start the interrupt and wait for about 4096 samples
adc.interrupt_state(p20,1);
wait(.2);
//Finsh up - Unset pin 20
adc.interrupt_state(p20,0);
adc.setup(p20,0);
int actual_rate = adc.actual_sample_rate();
//for debugging tell the terminal sample rate and how many samples we took
printf("Requested max sample rate is %u, actual max sample rate is %u.\n",
SAMPLE_RATE, actual_rate);
printf("We did %i samples\n",Counter);
high = 0;
low = 0;
for (int i=3; i<46; i+=3) {
high1 = goertzelFilter(Buffer, (note + i ), Counter);
if (high1 > high) high=high1;
}
for (int i=3; i<46; i+=3) {
low1 = goertzelFilter(Buffer, (note - i ), Counter);
if (low1 > low) low=low1;
}
in_tune1 = goertzelFilter(Buffer, (note+1), Counter);
in_tune2 = goertzelFilter(Buffer, note, Counter);
in_tune3 = goertzelFilter(Buffer, (note-1), Counter);
if ((in_tune1 > in_tune2) && (in_tune1 > in_tune3)) in_tune = in_tune1;
else if ((in_tune2 > in_tune1) && (in_tune2 > in_tune3)) in_tune = in_tune2;
else in_tune = in_tune3;
printf("high = %.2f, low = %.2f, in_tune = %.2f", high, low, in_tune);
high_mod = high/in_tune;
low_mod = low/in_tune;
if ((high_mod > .8)&&(low_mod > .8)) {
myled1 = 0;
myled2 = 0;
myled3 = 0;
}
if ((high_mod < .8)&&(low_mod > .8)) {
myled1 = 0;
myled2 = 0;
myled3 = 0;
}
if ((high > in_tune) && (low < in_tune)) { //Guitar string is at correct frequency
myled1 = 0;
myled2 = 1;
myled3 = 0;
} else if (high > in_tune) { // String is higher than the desired frequency
myled1 = 1;
myled2 = 0;
myled3 = 0;
} else if (low < in_tune){ // String is below that of the desired frequency
myled1 = 0;
myled2 = 0;
myled3 = 1;
} else { // Else no input, display squiggles
myled1 = 0;
myled2 = 0;
myled3 = 0;
}
// Display on the LCD
lcd.cls();
lcd.locate(0,0);
lcd.printf("Tuning String: %i", (6-string_select));
lcd.locate(0,11);
lcd.printf("%s at %i Hz",key, (int) note);
lcd.locate(0,23);
if (myled2) lcd.printf("In Tune!"); // if myled2 is on, guitar is in tune
else if (myled3) lcd.printf("Too Low "); // if myled3 is on, guitar is lower than desired tone
else if (myled1) lcd.printf("Too High"); // if myled1 is on, guitar is higher than desired tone
else lcd.printf("No Input Detected"); // corresponds to led case 4 - no guitar input present
Counter = 0;
}
}
Now, when I compile the program, the messages print. However, the LEDs alternate between "Too high", "Too low", and to the squiggles. I'll be the first to admit I'm not the best coder, something I am really going to work on over the Summer. There could be a very significant problem with the code, or it may be something relatively simple. All inputs are appreciated.
Note: I have not connected the debounce circuit on the breadboard - it is connected to the Nokia LCD which I did not use. I am able to change the desired string, between 1-6, using the joystick on the mbed application board instead. Is that okay? Or is the debounce circuit of pivotal importance?
Thanks again.
The loop which finds low is (almost) the same as the loop which finds high in this code:
high = 0;
low = 0;
for (int i=3; i<46; i+=3) {
high1 = goertzelFilter(Buffer, (note + i ), Counter);
if (high1 > high) high=high1;
}
for (int i=3; i<46; i+=3) {
low1 = goertzelFilter(Buffer, (note - i ), Counter);
if (low1 > low) low=low1;
}
I suggest the low part should be like this
low = FLT_MAX;
for (int i=3; i<46; i+=3) {
low1 = goertzelFilter(Buffer, (note - i ), Counter);
if (low1 < low) low=low1;
}
...although I could be mistaken at the intended use of low.
Related
I am trying to make brown noise in C++, and to play the sound of it. You can hear the brown noise, but I constantly hear clicking in the background and I don't know why.
Here is my code:
#include <xaudio2.h>
#include <iostream>
#include <random>
using namespace std;
#define PI2 6.28318530717958647692f
#define l 2205 //0.05 seconds
bool init();
bool loop();
random_device rd;
mt19937 gen(rd());
uniform_real_distribution<> dis(-.01, .01);
IXAudio2MasteringVoice* pMasterVoice;
IXAudio2* pXAudio2;
IXAudio2SourceVoice* pSourceVoice;
XAUDIO2_BUFFER buffer;
WAVEFORMATEX wfx;
XAUDIO2_VOICE_STATE state;
BYTE pDataBuffer[2*l];
BYTE bytw[2];
int pow16[2];
float w[l];
int frame, p;
float tt, ampl;
bool loop() {
w[0] = w[l - 1] + dis(gen)*ampl;
for (int t = 1; t < l; t++) {
tt = (float)(t + frame*l); //total time
w[t] = w[t - 1] + dis(gen)*ampl;
if (w[t] > ampl) {
cout << "upper edge ";
w[t] = ampl - fmod(w[t], ampl);
}
if (w[t] < -ampl) {
cout << "lower edge ";
w[t] = -fmod(w[t], ampl) - ampl;
}
//w[t] = sin(PI2*tt/p)*ampl;
//w[t] = (fmod(tt/p, 1) < .5 ? ampl : -ampl)*(.5f - 2.f*fmod(tt/p, .5f));
int intw = (int)w[t];
if (intw < 0) {
intw += 65535;
}
bytw[0] = 0; bytw[1] = 0;
for (int k = 1; k >= 0; k--) {
//turn integer into a little endian byte array
bytw[k] += (BYTE)(16*(intw/pow16[k]));
intw -= bytw[k]*(pow16[k]/16);
bytw[k] += (BYTE)(intw/(pow16[k]/16));
intw -= (intw/(pow16[k]/16))*pow16[k]/16;
}
pDataBuffer[2*t] = bytw[0];
pDataBuffer[2*t + 1] = bytw[1];
}
cout << endl << endl;
if (frame > 1) {
//wait until the current one is done playing
while (pSourceVoice->GetState(&state), state.BuffersQueued > 1) {}
}
buffer.AudioBytes = 2*l; //number of bytes per buffer
buffer.pAudioData = pDataBuffer;
buffer.Flags = XAUDIO2_END_OF_STREAM;
pSourceVoice->SubmitSourceBuffer(&buffer);
if (frame == 1) {
pSourceVoice->Start(0, 0);
}
frame++;
return true;
}
bool init() {
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
pXAudio2 = nullptr;
XAudio2Create(&pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR);
pMasterVoice = nullptr;
pXAudio2->CreateMasteringVoice(&pMasterVoice);
wfx = {0};
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = (WORD)1; //mono
wfx.nSamplesPerSec = (DWORD)44100; //samplerate
wfx.wBitsPerSample = (WORD)16; //16 bit (signed)
wfx.nBlockAlign = (WORD)2; //2 bytes per sample
wfx.nAvgBytesPerSec = (DWORD)88200; //samplerate*blockalign
wfx.cbSize = (WORD)0;
pSourceVoice = nullptr;
pXAudio2->CreateSourceVoice(&pSourceVoice, &wfx);
tt = 0, p = 1000, ampl = 10000;
pow16[0] = 16;
pow16[1] = 4096;
frame = 0;
return true;
}
int main() {
if (!init()) return 1;
cout << "start";
while (loop()) {}
return 0;
}
The line before the for-loop in loop() is to make sure that the first element nicely attaches itself to the last element of the previous iteration.
To make sure that w doesn't go over ampl or under -ampl, I have added a couple lines that make them bounce back, and I make it output "upper edge" or "lower edge" respectively so that you know when this is happening. As you notice, the clicking also happens when the w is not near the edges.
As a test to make sure it isn't because of XAudio2 being implemented wrongly, you can comment the first line in loop() that defines the first element of w; make the for-loop (in the next line) start from 0; comment the lines that create the brown noise; and uncomment one of the two lines after that: the first line to hear a sine wave sound, the second line to hear a square wave sound (both with a frequency of about 44100/1000 = 44.1 Hz, which you can change around by changing how p is initialized in init()). You will (hopefully) hear a clean sine/square wave sound.
So what is going wrong?
You have two issues in your code:
You only have a single buffer therefore its near impossible to submit a new buffer for playing quickly enough after the buffer stops playing for there to not be a gap between buffers. You are also modifying the buffer data whilst it is being played which will corrupt the output. You should use multiple buffers. With enough buffers this would also allow you to add some short sleeps to your while loop which is checking BuffersQueued to reduce the CPU usage.
You never set pDataBuffer[0] or pDataBuffer[1] so they will always be 0.
This code works:
#include <xaudio2.h>
#include <iostream>
#include <random>
#include <array>
#include <thread>
using namespace std;
#define PI2 6.28318530717958647692f
#define l 2205 //0.05 seconds
bool init();
bool loop();
random_device rd;
mt19937 gen(rd());
uniform_real_distribution<> dis(-.01, .01);
IXAudio2MasteringVoice* pMasterVoice;
IXAudio2* pXAudio2;
IXAudio2SourceVoice* pSourceVoice;
const size_t bufferCount = 64;
std::array<XAUDIO2_BUFFER, bufferCount> buffers;
WAVEFORMATEX wfx;
XAUDIO2_VOICE_STATE state;
std::array<std::array<BYTE,2 * l>, bufferCount> pDataBuffers;
BYTE bytw[2];
int pow16[2];
float w[l];
int frame, p;
float tt, ampl;
bool loop() {
float prevW = w[l - 1];
auto& pDataBuffer = pDataBuffers[frame & (bufferCount-1)];
auto& buffer = buffers[frame & (bufferCount - 1)];
for (int t = 0; t < l; t++) {
tt = (float)(t + frame * l); //total time
w[t] = prevW + dis(gen) * ampl;
if (w[t] > ampl) {
//cout << "upper edge ";
w[t] = ampl - fmod(w[t], ampl);
}
if (w[t] < -ampl) {
//cout << "lower edge ";
w[t] = -fmod(w[t], ampl) - ampl;
}
//w[t] = sin(PI2*tt/p)*ampl;
//w[t] = (fmod(tt/p, 1) < .5 ? ampl : -ampl)*(.5f - 2.f*fmod(tt/p, .5f));
prevW = w[t];
int intw = (int)w[t];
if (intw < 0) {
intw += 65535;
}
bytw[0] = 0; bytw[1] = 0;
for (int k = 1; k >= 0; k--) {
//turn integer into a little endian byte array
bytw[k] += (BYTE)(16 * (intw / pow16[k]));
intw -= bytw[k] * (pow16[k] / 16);
bytw[k] += (BYTE)(intw / (pow16[k] / 16));
intw -= (intw / (pow16[k] / 16)) * pow16[k] / 16;
}
pDataBuffer[2 * t] = bytw[0];
pDataBuffer[2 * t + 1] = bytw[1];
}
//cout << endl << endl;
if (frame > 1) {
//wait until the current one is done playing
while (pSourceVoice->GetState(&state), state.BuffersQueued > 1) { std::this_thread::sleep_for(std::chrono::milliseconds(1); }
}
buffer.AudioBytes = 2 * l; //number of bytes per buffer
buffer.pAudioData = pDataBuffer.data();
buffer.Flags = 0;
pSourceVoice->SubmitSourceBuffer(&buffer);
if (frame == 1) {
pSourceVoice->Start(0, 0);
}
frame++;
return true;
}
bool init() {
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
pXAudio2 = nullptr;
XAudio2Create(&pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR);
pMasterVoice = nullptr;
pXAudio2->CreateMasteringVoice(&pMasterVoice);
wfx = { 0 };
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = (WORD)1; //mono
wfx.nSamplesPerSec = (DWORD)44100; //samplerate
wfx.wBitsPerSample = (WORD)16; //16 bit (signed)
wfx.nBlockAlign = (WORD)2; //2 bytes per sample
wfx.nAvgBytesPerSec = (DWORD)88200; //samplerate*blockalign
wfx.cbSize = (WORD)0;
pSourceVoice = nullptr;
pXAudio2->CreateSourceVoice(&pSourceVoice, &wfx);
tt = 0, p = 1000, ampl = 10000;
pow16[0] = 16;
pow16[1] = 4096;
frame = 0;
return true;
}
int main() {
if (!init()) return 1;
while (loop()) {}
return 0;
}
I haven't tried to follow all of your logic but it seems over complicated and could definitely be simplified.
The massive use of global variables is also not a great way to write a program. You should move variables inside the functions where possible, otherwise either pass them to the function as arguments or use a class to hold the state.
I'm coding Arduino, but I'm confused about combining 2 sensors and 1 servo with a push button. I hope someone can help me.
I have made one by one the sensor coding and it works, but I want to combine them into one program.
// code void loop water temperatur sensor
void loop(void`{
sensors.requestTemperatures();
Celcius = sensors.getTempCByIndex(0);
Serial.print(Celcius);
Serial.println(" C ");
delay(1000);
}
// this code push button with servo
// code void servo with push button
void loop() {
if (digitalRead(pushButtonPin) == LOW) {
buttonPushed = 1;
Serial.println("Servo ON");
delay(1000);
}
if (buttonPushed) {
// change the angle for next time through the loop:
angle = angle + angleStep;
// reverse the direction of the moving at the ends of the angle:
if (angle >= maxAngle) {
angleStep = -angleStep;
if (type == 1) {
buttonPushed =0;
}
}
if (angle <= minAngle) {
angleStep = -angleStep;
if (type == 2) {
buttonPushed =0;
}
}
myservo.write(angle); // move the servo to desired angle
delay(100); // waits for the servo to get there
}
}
// Ph Sensor code
void loop(void) {
static unsigned long samplingTime = millis();
static unsigned long printTime = millis();
static float pHValue, voltage;
if (millis() - samplingTime > samplingInterval) {
pHArray[pHArrayIndex++] = analogRead(SensorPin);
if (pHArrayIndex==ArrayLenth)
pHArrayIndex=0;
voltage = avergearray(pHArray, ArrayLenth) * 5.0 / 1024;
pHValue = 3 * voltage + Offset;
samplingTime=millis();
}
if (millis() - printTime > printInterval) { //Every 800 milliseconds, print a numerical, convert the state of the LED indicator
Serial.print("Voltage:");
Serial.print(voltage, 2);
Serial.print(" pH value: ");
Serial.println(pHValue, 2);
digitalWrite(LED, digitalRead(LED) ^ 1);
printTime = millis();
}
}
double avergearray(int* arr, int number){
int i;
int max, min;
double avg;
long amount = 0;
if (number <= 0) {
Serial.println("Error number for the array to avraging!/n");
return 0;
}
if (number<5) { //less than 5, calculated directly statistics
for (i=0; i<number; i++) {
amount += arr[i];
}
avg = amount / number;
return avg;
} else {
if (arr[0] < arr[1]) {
min = arr[0];
max = arr[1];
} else {
min = arr[1];
max = arr[0];
}
for (i=2; i<number; i++) {
if (arr[i] < min) {
amount += min; //arr<min
min = arr[i];
} else {
if (arr[i] > max) {
amount += max; //arr>max
max = arr[i];
} else {
amount += arr[i]; //min<=arr<=max
}
} //if
} //for
avg = (double)amount / (number - 2);
} //if
return avg;
}
Your "Ph Sensor code" demonstrates how to do 2 things at different time intervals in one loop.
void loop() {
if (/* time is right to do thing 1 */) {
// do thing 1
}
if (/* time is right to do thing 2 */) {
// do thing 2
}
}
This is called a state machine. You can extend this logic to do 4 or more things in one loop.
Obviously you can't use delay as it blocks the entire loop, preventing other work from continuing. So you need to convert the first two loops into the structure similar to the one above. Then you will be able to merge all the loops into a single one.
I have this problem where I try to set an output pin high for a set time and times.
I do the call with hapticFeedback(1000, 2, 1);
the variables are defined as
unsigned long hapticPreviousMillis = 0;
int hapticState = LOW;
int oneshotHaptic = 0;
here is the function. For some reason I only get the pin set HIGH and not the blinks and LOW
void hapticFeedback(int activeLength, int repeats, int oneshotHaptic) {
if (oneshotHaptic == 1) {
for (int x = 0; x <= repeats; x++) {
unsigned long currentMillis = millis();
if (currentMillis - hapticPreviousMillis >= (unsigned long)activeLength) {
hapticPreviousMillis = currentMillis;
if (hapticState == LOW) {
hapticState = HIGH;
}
else {
hapticState = LOW;
}
digitalWrite(haptic, hapticState);
}
}
}
oneshotHaptic = 0;
}
So I figured it out and if anyone else is looking for this here is what I came up with. It might not be the smoothest of code but it does what I intended it to do
in the loop I have
if (setOneshotHaptic == 1) {
hapticFeedback(activeLength);
}
and the haptic function look like this
void hapticFeedback(int activeLength) {
unsigned long currentMillis = millis();
if (currentMillis - hapticPreviousMillis >= (unsigned long)activeLength) {
hapticPreviousMillis = currentMillis;
if (x == repeats) {
setOneshotHaptic = false;
hapticState = HIGH;
x = 0;
}
if (hapticState == LOW) {
hapticState = HIGH;
x++;
}
else {
hapticState = LOW;
}
digitalWrite(haptic, hapticState);
}
}
whenever i like to have haptic feedback i can define the following vars
setOneshotHaptic = true;
repeats = 3;
activeLength = 1000;
When the number of repeats has been reached I lay down the oneshot, force the output to high for it to be low by the routine and finally reset my repeat counter.
There might be nicer ways to do this. However I couldn't find them and this works for me....
The program's supposed to constantly listen for 433 MHz messages coming from soil moisture sensors in plant pots and decide whether or not to activate a pump based on those readings.
Additionally it's supposed to check wired water sensors while it's listening.
All 433 MHz Received Messages should be stored within the array sensor_data[i].
At startup the positions 1to NUM_Sensors(in this case 3) is filled with the constant int NO_DATA (500).
The Problem is, that for some reason I get corrupted Number in the array number 3:
Serial Print:
Wired Flower Pots Checked
All Sensors or Timeout reached
Array_Print: 500
Array_Print: 500
Array_Print: 30001
In this case the 30001 appears in the array with no specific reason (I guess).. there's no received message.
Here's the code changed to the minimum so that the error occurs:
#include <RCSwitch.h>
RCSwitch mySwitch = RCSwitch();
//Receiver Setup for wireless soil moisture readings
unsigned long last_ground_check = 0;
const int NUM_SENSORS = 3;
const uint32_t SENSOR_TIMEOUT = 30000;
int sensor_data[NUM_SENSORS];
uint32_t last_message_time = 0;
uint32_t elapsed = 0;
float total_value = 0;
float real_value = 0;
int count = 0;
const int NO_DATA = 500;
boolean received = false;
//###################//
void setup()
{
Serial.begin(9600);
mySwitch.enableReceive(INT1); // Interrupt 2 = Pin 2
delay(1500);
Serial.println("<><><><><><><><><><><><>");
Serial.println(" Start ");
Serial.println("<><><><><><><><><><><><>");
for (int i = 0; i <= NUM_SENSORS; i++) {
sensor_data[i] = NO_DATA;
}
} // Setup END
void loop()
{
if (received == false) {
if (millis() - last_ground_check > 10000) {
Serial.println("Checking Wired Flower Pot");
}
Serial.println("Wired Flower Pots Checked");
last_ground_check = millis();
}
if (mySwitch.available()) { // Start whenever a 433 MHz Message is received
received = true;
double value = mySwitch.getReceivedValue();
delay(1000);
int sensor_id = 1;
int sensor_value = 2;
if (sensor_value >= 0 && sensor_value <= 100) {
sensor_data[sensor_id] = sensor_value;
last_message_time = millis();
mySwitch.resetAvailable();
}
}
byte sensors_reported = 0;
for (int i = 0; i <= NUM_SENSORS; i++) {
if (NO_DATA != sensor_data[i]) {
sensors_reported += 1; // CODE Gets here because of corrupted Array Value although no message was received
}
}
if (sensors_reported != 0) {
uint32_t elapsed = millis() - last_message_time;
if (NUM_SENSORS == sensors_reported || elapsed > SENSOR_TIMEOUT) {
Serial.println("All Sensors or Timeout reached");
for (int i = 1; i <= NUM_SENSORS; i++) {
Serial.print("Array_Print: ");
Serial.println(sensor_data[i]);
}
for (int i = 1; i <= NUM_SENSORS; i++) {
if (sensor_data[i] < NO_DATA) {
count++;
total_value += sensor_data[i];
}
}
real_value = total_value / count;
Serial.print("Soil Moisture: ");
Serial.println(real_value);
if (real_value <= 20) {
//Set Pump ON
}
for (int i = 1; i <= NUM_SENSORS; i++) {
sensor_data[i] = NO_DATA;
}
total_value = 0;
real_value = 0;
sensors_reported = 0;
count = 0;
received = false;
Serial.println("RESET #### RESET ####");
delay(5000);
}
}
} //LOOP
You first allocate an array of size NUM_SENSORS=3, and then you continue to use it as if it was of size 4.
Your array has 3 elements sensor_data[0], [1] and [2]. Your loop condition i <= NUM_SENSORS results in accessing sensor_data[3] which is just some memory after your last array element. Even if you set sensor_data[3] in your setup, if the same memory is referenced by some other variable, the NO_DATA will be overwritten.
Index your loops over the data array from i = 0 to i < NUM_SENSORS.
i'm moving outside my confront zone and trying to make a random number distribution program while also making sure it is still somewhat uniform.
here is my code
this is the RandomDistribution.h file
#pragma once
#include <vector>
#include <random>
#include <iostream>
static float randy(float low, float high) {
static std::random_device rd;
static std::mt19937 random(rd());
std::uniform_real_distribution<float> ran(low, high);
return ran(random);
}
typedef std::vector<float> Vfloat;
class RandomDistribution
{
public:
RandomDistribution();
RandomDistribution(float percent, float contents, int container);
~RandomDistribution();
void setvariables(float percent, float contents, int container);
Vfloat RunDistribution();
private:
float divider;
float _percent;
int jar_limit;
float _contents;
float _maxdistribution;
Vfloat Jar;
bool is0;
};
this is my RandomDistribution.cpp
#include "RandomDistribution.h"
RandomDistribution::RandomDistribution() {
}
RandomDistribution::RandomDistribution(float percent, float contents, int containers):_contents(contents),jar_limit(containers)
{
Jar.resize(containers);
if (percent < 0)
_percent = 0;
else {
_percent = percent;
}
divider = jar_limit * percent;
is0 = false;
}
RandomDistribution::~RandomDistribution()
{
}
void RandomDistribution::setvariables(float percent, float contents, int container) {
if (jar_limit != container)
Jar.resize(container);
_contents = contents;
jar_limit = container;
is0 = false;
if (percent < 0)
_percent = 0;
else {
_percent = percent;
}
divider = jar_limit * percent;
}
Vfloat RandomDistribution::RunDistribution() {
for (int i = 0; i < jar_limit; i++) {
if (!is0) {
if (i + 1 >= jar_limit || _contents < 2) {
Jar[i] = _contents;
_contents -= Jar[i];
is0 = true;
}
if (!_percent <= 0) {//making sure it does not get the hole container at once
_maxdistribution = (_contents / (divider)) * (i + 1);
}
else {
_maxdistribution = _contents;
}
Jar[i] = randy(0, _maxdistribution);
if (Jar[i] < 1) {
Jar[i] = 0;
continue;
}
_contents -= Jar[i];
}
else {
Jar[0];
}
//mixing Jar so it is randomly spaced out instead all at the top
int swapper = randy(0, i);
float hold = Jar[i];
Jar[i] = Jar[swapper];
Jar[swapper] = hold;
}
return Jar;
}
source code
int main(){
RandomDistribution distribution[100];
for (int i = 0; i < 100; i++) {
distribution[i] = {RandomDistribution(1.0f, 5000.0f, 2000) };
}
Vfloat k;
k.resize(200);
for (int i = 0; i < 10; i++) {
auto t3 = chrono::steady_clock::now();
for (int b = 0; b < 100; b++) {
k = distribution[b].RunDistribution();
distribution[b].setvariables(1.0f, 5000.0f, 2000);
}
auto t4 = chrono::steady_clock::now();
auto time_span = chrono::duration_cast<chrono::duration<double>>(t4 - t3);
cout << time_span.count() << " seconds\n";
}
}
what prints out is usually between 1 to 2 seconds for each cycle. i want to bring it down to a tenth of a second if possible cause this is gonna be only one step of the process to completion and i want to run it alot more then 100 times. what can i do to speed this up, any trick or something i'm just missing here.
here is a sample of the time stamps
4.71113 seconds
1.35444 seconds
1.45008 seconds
1.74961 seconds
2.59192 seconds
2.76171 seconds
1.90149 seconds
2.2822 seconds
2.36768 seconds
2.61969 seconds
Cheinan Marks has some benchmarks and performance tips related to random generators & friends in his cppcon 2016 talk I Just Wanted a Random Integer! He mentions some fast generators as well IIRC. I'd start there.