Implemented command line argument parser

This commit is contained in:
Sam Perry
2017-02-12 19:28:41 +00:00
parent 46a8d773dd
commit 234b0a7e34
9 changed files with 131 additions and 63 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
#!/usr/bin/env bash
../Bela/scripts/build_project.sh ./src/ -p Assignment_1
../Bela/scripts/build_project.sh ./src/ -p Assignment_1
-3
View File
@@ -1,3 +0,0 @@
#!/usr/bin/env bash
../Bela/scripts/run_project.sh Assignment_1
Executable
+3
View File
@@ -0,0 +1,3 @@
#!/usr/bin/env bash
../Bela/scripts/run_project.sh Assignment_1 -c "-f 1000"
Executable
+3
View File
@@ -0,0 +1,3 @@
#!/usr/bin/env bash
../Bela/scripts/run_project.sh Assignment_1 -c "-f 3000"
Executable
+3
View File
@@ -0,0 +1,3 @@
#!/usr/bin/env bash
../Bela/scripts/run_project.sh Assignment_1 -c "-f 1000 --linkwitzriley"
Executable
+3
View File
@@ -0,0 +1,3 @@
#!/usr/bin/env bash
../Bela/scripts/run_project.sh Assignment_1 -c "-f 3000 --linkwitzriley"
+85 -32
View File
@@ -5,7 +5,9 @@
class Filter {
public:
Filter(float crossoverFrequency, float fs, bool highpass=false, bool linkwitzRiley=false) {
Filter(float crossoverFrequency, float fs, bool highpass=false, bool linkwitzRiley=true) {
// Filter class constructor is used for the calculation of filter
// coefficients and delay line memory allocation.
// Calculate ratio between cutoff frequency and sampling rate
double wc = crossoverFrequency/fs;
@@ -16,64 +18,104 @@ class Filter {
// Warp the frequency to convert from continuous to discrete time cutoff
double wd1 = 1.0 / tan(M_PI*wc);
// Calculate coefficients from equation
// Calculate coefficients from equation and store in a vector
numerator.push_back(1.0 / (1.0 + q*wd1 + pow(wd1, 2)));
numerator.push_back(2 * numerator[0]);
numerator.push_back(numerator[0]);
denominator.push_back(1.0);
denominator.push_back(-2.0 * (pow(wd1, 2) - 1.0) * numerator[0]);
denominator.push_back((1.0 - q * wd1 + pow(wd1, 2)) * numerator[0]);
// If the filter is a high pass filter, convert numerator
// coefficients to reflect this
if(highpass) {
numerator[0] = numerator[0] * pow(wd1, 2);
numerator[1] = -numerator[1] * pow(wd1, 2);
numerator[2] = numerator[2] * pow(wd1, 2);
}
// If the filter is using the Linkwitz-Riley filter structure,
// convolve the numerator and denominator generated for the 2nd
// order butterworth filter with themselves. This creates the 5
// coefficients of 2 cascaded 2nd order butterworth filters needed
// for this filter structure.
if(linkwitzRiley) {
//rt_printf("Num size: %d\n", numerator.size());
//rt_printf("Den size: %d\n", denominator.size());
numerator = convolve(numerator, numerator);
denominator = convolve(denominator, denominator);
rt_printf("Num size: %d\n", numerator.size());
rt_printf("Den size: %d\n", denominator.size());
rt_printf("%f %f %f %f %f\n", numerator[0], numerator[1], numerator[2], numerator[3], numerator[4]);
rt_printf("%f %f %f %f %f\n", denominator[0], denominator[1], denominator[2], denominator[3], denominator[4]);
}
else {
rt_printf("Numerator: %f %f %f\nDenominator: %f %f %f\nHighpass: %s\n",
numerator[0],
numerator[1],
numerator[2],
denominator[0],
denominator[1],
denominator[2],
highpass ? "true":"false");
//
// Print coefficients to the console
rt_printf("\nNumerator:\t\t%f %f %f %f %f\nDenominator:\t\t%f %f %f %f %f\nCrossover Frequency:\t%f\nHighpass:\t\t%s\nFilter Type:\t\t%s\n\n",
numerator[0],
numerator[1],
numerator[2],
numerator[3],
numerator[4],
denominator[0],
denominator[1],
denominator[2],
denominator[3],
denominator[4],
crossoverFrequency,
highpass ? "true" : "false",
linkwitzRiley ? "4th-Order Linkwitz-Riley" : "2nd-Order Butterworth");
} else {
// Print coefficients to the console
rt_printf("\nNumerator:\t\t%f %f %f\nDenominator:\t\t%f %f %f\nCrossover Frequency:\t%f\nHighpass:\t\t%s\nFilter Type:\t\t%s\n\n",
numerator[0],
numerator[1],
numerator[2],
denominator[0],
denominator[1],
denominator[2],
crossoverFrequency,
highpass ? "true" : "false",
linkwitzRiley ? "4th-Order Linkwitz-Riley" : "2nd-Order Butterworth");
}
// Allocate memory for delay line based on the number of
// coefficients generated. Initialize vectors with values of 0.
inputDelayBuf.assign(int(numerator.size()), 0.0);
outputDelayBuf.assign(int(denominator.size()), 0.0);
// Initialize delay buffer to be two samples long for the
// second-order Butterworth filter
// Store the delay size of delay buffers
inputDelaySize = inputDelayBuf.size();
outputDelaySize = outputDelayBuf.size();
}
float applyFilter(float x0) {
float applyFilter(const float &x0) {
// Increment the write pointer of the delay buffer storing input
// samples
++inputDelayBufWritePtr;
// Wrap values to withink size of buffer. Prevents an integer
// overflow
inputDelayBufWritePtr = (inputDelayBufWritePtr+inputDelaySize)%inputDelaySize;
// Increment the write pointer of the delay buffer storing output
// samples
++outputDelayBufWritePtr;
// Wrap values to withink size of buffer. Prevents an integer
// overflow
outputDelayBufWritePtr = (outputDelayBufWritePtr+outputDelaySize)%outputDelaySize;
// Set the current value of the input delay buffer to the value of
// the sample provided to the function
inputDelayBuf[(inputDelayBufWritePtr+inputDelaySize)%inputDelaySize] = x0;
// Initialize a variable to store an output value
float y = 0;
// Accumulate each sample in the input delay buffer, multiplied by
// it's corresponding coefficient
for(unsigned int i = 0; i < inputDelaySize; i++) {
y += inputDelayBuf[(inputDelayBufWritePtr-i+inputDelaySize)%inputDelaySize] * numerator[i];
}
// decumulate each sample in the output delay buffer (aside from
// the current index), multiplied by it's corresponding coefficient
for(unsigned int i = 1; i < outputDelaySize; i++) {
y -= outputDelayBuf[(outputDelayBufWritePtr-i+outputDelaySize)%outputDelaySize] * denominator[i];
}
// Scale by first coefficient in the denominator (always 1 in
// current implementation, so added only for generalization of the
// method for future use)
y /= denominator[0];
// Store the calculated output sample in the output sample delay
// buffer
outputDelayBuf[(outputDelayBufWritePtr+outputDelaySize)%outputDelaySize] = y;
return y;
@@ -95,17 +137,28 @@ class Filter {
// Convolution function adapted from: http://stackoverflow.com/questions/24518989/how-to-perform-1-dimensional-valid-convolution
template<typename T>
std::vector<T> convolve(std::vector<T> const &f, std::vector<T> const &g) {
int const nf = f.size();
int const ng = g.size();
int const n = nf + ng - 1;
std::vector<T> out(n, T());
for(auto i(0); i < n; ++i) {
int const jmn = (i >= ng - 1)? i - (ng - 1) : 0;
int const jmx = (i < nf - 1)? i : nf - 1;
for(auto j(jmn); j <= jmx; ++j) {
out[i] += (f[j] * g[i - j]);
// Calculate the size of input vectors
int const nf = f.size();
int const ng = g.size();
// Calculate the size of output vector as the combined size of both
// input vectors, minus 1
int const n = nf + ng - 1;
// Initialize vector of the same input type as input vectors
// Allocate memory for all elements of the output to be calculated
std::vector<T> out(n, T());
// For each output element...
for(auto i(0); i < n; ++i) {
// Calculate minimum and maximum indexes to iterate over each
// vector
int const jmn = (i >= ng - 1)? i - (ng - 1) : 0;
int const jmx = (i < nf - 1)? i : nf - 1;
// Accumulate the multiplication of elements in both vectors,
// based on the indexes calculated, to give the output value at
// the current output index
for(auto j(jmn); j <= jmx; ++j) {
out[i] += (f[j] * g[i - j]);
}
}
}
return out;
return out;
}
};
+15 -7
View File
@@ -18,6 +18,8 @@
#include <getopt.h>
#include <Bela.h>
#include "userOptions.h"
using namespace std;
// Handle Ctrl-C by requesting that the audio rendering stop
@@ -39,12 +41,15 @@ void usage(const char * processName)
int main(int argc, char *argv[])
{
BelaInitSettings settings; // Standard audio settings
float frequency = 5000.0; // Frequency of crossover
// Initialize default values for command line options
UserOpts uOpts = {1000.0, false};
struct option customOptions[] =
{
{"help", 0, NULL, 'h'},
{"frequency", 1, NULL, 'f'},
{"linkwitzriley", 0, NULL, 'l'},
{NULL, 0, NULL, 0}
};
@@ -61,12 +66,15 @@ int main(int argc, char *argv[])
usage(basename(argv[0]));
exit(0);
case 'f':
frequency = atof(optarg);
if(frequency < 20.0)
frequency = 20.0;
if(frequency > 5000.0)
frequency = 5000.0;
uOpts.frequency = atof(optarg);
if(uOpts.frequency < 20.0)
uOpts.frequency = 20.0;
if(uOpts.frequency > 5000.0)
uOpts.frequency = 5000.0;
break;
case 'l':
uOpts.linkwitzRiley = true;
break;
case '?':
default:
usage(basename(argv[0]));
@@ -75,7 +83,7 @@ int main(int argc, char *argv[])
}
// Initialise the PRU audio device
if(Bela_initAudio(&settings, &frequency) != 0) {
if(Bela_initAudio(&settings, &uOpts) != 0) {
cout << "Error: unable to initialise audio" << endl;
return -1;
}
+18 -20
View File
@@ -17,6 +17,7 @@
#include <Scope.h>
#include "filter.h"
#include "userOptions.h"
/* TASK: declare any global variables you need here */
@@ -29,30 +30,30 @@
//
// Return true on success; returning false halts the program.
// Create filter objects using unique pointers for automatic memory
// deallocation
std::unique_ptr<Filter> gLowPass;
std::unique_ptr<Filter> gHighPass;
// instantiate the scope
// Instantiate the scope
Scope scope;
bool setup(BelaContext *context, void *userData)
{
// Set the Bela IDE scope to read two outputs
scope.setup(2, context->audioSampleRate);
// Set default values for when command line arguments are not provided by
// the user
float crossoverFrequency = 1000.0;
bool linkwitzRiley = false;
// Retrieve a parameter passed in from the initAudio() call
if(userData != 0)
crossoverFrequency = *(float *)userData;
crossoverFrequency = (*(UserOpts *)userData).frequency;
linkwitzRiley = (*(UserOpts *)userData).linkwitzRiley;
/* TASK:
* Calculate the filter coefficients based on the given
* crossover frequency.
*
* Initialise any previous state (clearing buffers etc.)
* to prepare for calls to render()
*/
gLowPass.reset(new Filter(crossoverFrequency, context->audioSampleRate));
gHighPass.reset(new Filter(crossoverFrequency, context->audioSampleRate, true));
// Create a low-pass and high-pass filter object using the crossover
// frequency and filter design specified by CLI arguments
gLowPass.reset(new Filter(crossoverFrequency, context->audioSampleRate, false, linkwitzRiley));
gHighPass.reset(new Filter(crossoverFrequency, context->audioSampleRate, true, linkwitzRiley));
return true;
}
@@ -79,11 +80,12 @@ void render(BelaContext *context, void *userData)
// Convert input to mono
float monoSamp = (leftIn + rightIn) * 0.5;
// Apply filter to current sample for both channels.
// filtering/buffering is handeled within the Filter objects.
float leftOut = gLowPass->applyFilter(monoSamp);
float rightOut = gHighPass->applyFilter(monoSamp);
//leftOut = monoSamp;
//rightOut = monoSamp;
// Plot the output of the filters to the IDE scope
scope.log(leftOut, rightOut);
// Write the sample into the output buffer
audioWrite(context, n, 0, leftOut);
@@ -96,9 +98,5 @@ void render(BelaContext *context, void *userData)
void cleanup(BelaContext *context, void *userData)
{
/* TASK:
* If you allocate any memory, be sure to release it here.
* You may or may not need anything in this function, depending
* on your implementation.
*/
// Cleanup wasn't necessary through the use of unique pointers.
}