Sidebar Menu

Projects

  • Dashboard
  • Research Project
  • Milestones
  • Repository
  • Tasks
  • Time Tracking
  • Designs
  • Forum
  • Users
  • Activities

Login

  • Login
  • Webmail
  • Admin
  • Downloads
  • Research

Twitter

Posts by stumathews
Stuart Mathews
  • Home
  • Blog
  • Code
  • Running
  • Gaming
  • Research
  • About
    • Portfolio
    • Info

Convolution, Running and Finite State Machines

Details
Category: Code
By Stuart Mathews
Stuart Mathews
08.Mar
08 March 2020
Last Updated: 02 April 2020
Hits: 4828
  • Running
  • C++
  • Math
  • Digital Signal Processing and Audio Programming

This weekend I spent most of it trying to create a Finite Impulse Response filter in C++ using fmod, a audio library.

The main way to manipulate a carrier signal or some input signal like some sound or music playing and to say remove the high-frequency components is that you need to perform a mathematical processed called convolution, whereby you 'convolve' the input with what is called filter coefficients that adapt and modify the input amplitudes such that they filter out and remove those components. The convolution theorem looks like this:

This specifically was the task:

Program a controllable FIR filter, i.e. a filter that changes its characteristics over time in response to a control signal. To change in the characteristics of the filter, you need to change the filter coefficients. If you do the games track, implement the filter for real time use with a circular buffer in an FMOD custom DSP and implement a trigger in the demo that changes the filter

The thing that I needed to do was implement a circular buffer to store previous ie historical samples so that the 'b' coefficients, which require previous samples to work with can be applied to them.

Digital Signal Processing is quite an interesting subject, there is a great book, called The Scientist and Engineer's Guide to Digital Signal Processing that discusses many aspects of it here and it's quite comprehensive and it's free!

I think what I've enjoyed the most so far is actually the math, seeing the calculations translate into practical applications and I think this is what has helped me overcome some parts that were a bit daunting at first, especially the sinusoidal nature of signals, the Fourier transform and the complex number system.

So I spent most of that day coding up a circular buffer to store those samples so I can buffer audio that can be used for my filter. The theory behind circular buffers can be seen here: https://en.wikipedia.org/wiki/Circular_buffer, it's mostly quite clear and if you're going to be doing any type of real-time processing, its essential.

Once that was done, I was then able to use that buffer to store the previous n samples, apply the convolution to those previous samples and thereby in effect applying the filter to the input signal such that the output signal would hold those properties, which was to allow input frequencies of 0-1000 Hz through and removes those > 1000 Hz.

It sounds all a bit 'convolved' (see what I did there!) just to remove some frequencies from the signal but that's the point I think, to realise how much goes into it, and in doing the practicals you can appreciate the theory and test your understanding. 

I finally settled on a testing framework(Google Test) and used that to test it the implementation of my circular buffer. It's not production-ready by any stretch of the imagination because it doesn't really check bounds or other edge cases or anything silly that you try and do with it, however for the task at hand, it works just great.  So it's more of a prototype and a learning tool which perhaps might be a better way of putting it.

Anyway, this is how it ended up looking:

#pragma once
#include <vector>
using namespace std;

class CircularBufferTests_PreviousN_Test;

/*
A very simple circular buffer for my DSP assignment
*/
template <typename T>
class cbuf
{
	friend CircularBufferTests_PreviousN_Test; // For testing

private:
	int m_Size; // size of the buffer. It is fixed.
	vector<T> m_Buffer; // underlying buffer, buffer[0] is considered the first item
	int m_WriteIndex;
	int m_ReadIndex;

	// Internal functions for calulating indexes relative to n (last sample)
	int GetPrevNIndex(int n = 0) { return Wrap(GetNewestIndex() - n, m_Size); }
	int Wrap(int n, int arrayLength) { return ((n % arrayLength) + arrayLength) % arrayLength; }

public:
	cbuf(int size) :m_Size(size), m_WriteIndex(0), m_ReadIndex(0), m_Buffer(vector<T>(size)) {}
	
	~cbuf() { m_Buffer.clear(); m_Buffer = { 0 }; }

	// Expose the underlying vector for direct poiinter maipultion.
	// https://stackoverflow.com/questions/2923272/how-to-convert-vector-to-array
	T* ToArray() { return  &m_Buffer[0];	}

	// Overwrite oldest entry, returns the index of the item saved
	int Put(T item)
	{
		m_Buffer[GetWriteIndex()] = item;
		m_ReadIndex = GetWriteIndex();
		m_WriteIndex = (m_WriteIndex + 1) % m_Size;
		return m_ReadIndex;
	}

	// Read the value most recently added
	T ReadNewestHead() { return m_Buffer[m_ReadIndex]; }

	// Get the index of the last added item ie the newest
	int GetNewestIndex() { return m_ReadIndex; }

	// Read at a specific index. No bounds checking.
	T ReadAtIndex(int i) { return m_Buffer[i]; }

	// Read at value from the back of the buffer
	T ReadFromBack(int n = 0) { return m_Buffer[(m_Size - 1) - n]; }

	// Use index notation to reference items relative to the most recent item. ie n
	// Supports notation like -3 as in n-3 as well as 0 which means n
	// Not tested when n < 0
	T ReadN(int n = 0) { return m_Buffer[GetPrevNIndex(-n)]; }

	// Get the value that will be overwritten next
	T ReadOldest() { return m_Buffer[m_WriteIndex]; }

	// Get the index of the value that is going to be overwritten by next Put
	int GetOldIndex() { return m_WriteIndex; }

	// same as GetOldIndex()
	int GetWriteIndex() { return GetOldIndex(); }

	// Size of the underlying buffer.
	int GetSize() { return m_Buffer.size(); }

	void PrintContents()
	{
		cout << "[";
		for (int i = 0; i < m_Buffer.size(); i++)
		{
			std::cout << m_Buffer[i];
			if (i !a= m_Buffer.size())
				cout << ",";
		}
		cout << "]" << endl;;
	}
};

What I particularly like about my implementation is that you can refer to the last n values stored in it in the same way the algorithm refers to samples ie n-k, so you can see in my implementation of the convolution algorithm below, that I read the last n samples stored in the buffer using the notation: prevBuff->ReadN(-3) to signify x[n-3] ie the 3rd previous sample in x.

The convolution algorithm I put together to implement it is here and most of this is all on Github anyway: 

#pragma once
class ConvolutionHelper
{
public:
	// Swaps the signal and convolves the b coeficcients using previous values stored in circular buffer, updates the buffer
	static void ConvolveXn(float* chunk, const int numSamplesPerChunk, int n, float* yn, float* xn, float* bCoefficients, int num_coeff, cbuf<float>* prevBuff, float coefficientScale = 1)
	{		
		auto swappedXn = chunk[(numSamplesPerChunk - 1) - n];
		*yn = *xn;
		*yn = bCoefficients[0] * swappedXn; // x[n] * b[0]

		for (int b = 1; b <= num_coeff - 1; b++) // x[n-1] * b[1] etc...
			*yn += prevBuff->ReadN(-b) * (bCoefficients[b] * coefficientScale);

		prevBuff->Put(*xn);
	}
};

I pretty much got it to work and then went to bed, it was 2 am. 

Apart from that, I went for a nice long run, took with me a gel (orange flavour) and my headphones and ran through what was first torrential rain, then sunshine and the only thing I missed thankfully was snow. I did, unfortunately, tweak my calf again so I'm hoping this won't be another stint in the docks and that a good night's rest will cure it. If it doesn't well, I'll put plan b into action.

This is what happens when you stop running (I took the train back 3 stops) and then got out and continued to run from there. So I ran about say 12km in total including the bit between stations on the way home but started cold off the train, which probably is what did it. I must learn!

So the main run was about 11km, took about 55 mins, 54:40 to be precise and was pretty good - I'd just woken up from said coding night and needed to get out.

 

I ran with an average pace of just under 5 mins per Km at 4:50 which was fine, I ran back to Citrix as I quite like that route and it gives me a kick every time I run back and see the old manor house. 

I also got a new laptop, well if you can call it that. I needed hardware support for OpenGL 4 and my old trust machine can't support that. It's pretty much like my laptop but lighter, prettier, touch-screenier, and its got twice the disk space, and it supports OpenGL 4. I still use my laptop though, because it still has everything I want in a laptop minus OpenGL4. Also, its easier to use on the train as it's got a mouse built into the keyboard, a-la-thinkpad!

I'm developing another prototype game which needs to demonstrate good architecture practices (decoupling, design patterns etc) but I had no time to do it this weekend, and times running out! I've started it though, its got loading levels, a UI and menu system, collision detection, rudimentary AI which is the next thing I need to fix - I basically want my NPCs to have a mind of their own!

Also, my player seems to be able to walk through walls randomly which is a bug in my finite state machine. 

And finally some interesting things I saw this week:

Weird Duck Not Like The Others pic.twitter.com/kV23lpnwm7

— Life on Earth (@planetpng) March 7, 2020

And this one cracks me up too!

When he's still a pup at heart pic.twitter.com/n1nuGnKYrM

— Life on Earth (@planetpng) March 7, 2020

I've also been working on developing a prototype game in C# that demonstrates good architectural skills and implementation.

LanguageExt tutorial, games and timing

Details
Category: Code
By Stuart Mathews
Stuart Mathews
08.Aug
08 August 2019
Last Updated: 21 March 2020
Hits: 4525
  • Functional programming

If you're interested in the languageExt tutorial go to The languageExt Tutorial

I've been continuing to work on my Digital Forensics module and while I've not yet started doing the actual technical analysis, I have spent a lot of time planning for it.

One of the key parts of the assessment is performing a correct forensic investigation, meaning that there needs to be a methodic, sound and systematic way in which the investigation is carried out. I think this is where the potential for failure is the highest ie not performing due diligence. It's easy for anyone to jump straight in and start trying to solve the crime and I think this is what causes a lot of evidence to be inadmissible because there was no pre-thought and consideration for the repercussions.

So I'm slowly collecting my strategy and should hopefully have a template-checklist which will serve me well during my analysis phase.

Apart from that, I've continued to maintain my 4"30' pace every other day, 3 times a week, which is good. My weekly niggles seem to dissipate throughout the week, that is until I go to the gym again at the end of the week. I really should try not to put so much effort into one session - its probably going to cause an injury.

My last run was a bit quicker than usual at 4"30' pace:

 

I've had some time to work on practising and learning more about LanguageExt and I have developed a fairly wide-ranging step-by-step tutorial in github that is designed to help those uncomfortable with functional programming in C# to become better acclimatised to it. Its also helped me a great deal too.

I actually quite enjoyed doing it, and in the process, I have come to understand more than I did to begin with, which is a double-win.

I do quite enjoy the process of deconstructing ideas into their fundamentals and then building them back up again. Maybe that's why I like refactoring code and reverse engineering things. For instance, recently I've been learning about fractional powers and roots and like this tutorial, understanding the basics provides great insights when you've got to interpret something based on it and which is already at a higher, more complex/abstract level - which unfortunately is usually the preliminary view of most things.

Anyway back to my tutorial: I designed a new Monad type called a Box<T> which through iterative application of, first its construction and then its application really helps to build a good grounding of the basics. It's very much like understanding the fundamentals of anything, you need to understand them at their most essential purpose to realise their true potential and application.

This particular post on stack overflow during my research was pivotal. That being said, the layout of the tutorial looks like this:

The general tutorial is structured like this:

There are 36 tutorials

There are 36 tutorials and scope for more:

  • Partial Functions - Allowing multiple arguments to be 'baked' in and still appear as Math like functions (pure functions)
  • Immutability - Smart constructors, Immutable data-types
  • Caching
  • Threading and parallelism benefits
  • Guidelines for writing immutable code, starting with IO on the fringes (bicycle spoke design)
  • Immutable Collection types in Language Ext
  • Spoke and wheel model
  • Map, HashSet, Set LanguageExt collections
  • Changing state over time (Fold)
  • Custom useful Monad Extensions

Apart from that, I've been reading about math, and game programming.

I've bought some new books to add to my growing collection:

  1. Mathematics for 3D Game Programming & Computer Graphics
  2. Introduction to 3D game programming with DirectX10
  3. 3D Game programming using DirectX and OpenGL

So I'm pretty immersed in the material and this should substantially aid me in my various quests. If only I had more time. Between my digital forensics study, investment manager project, gym, running, my reading and work, there is sweet little left of it.

 

 

Lambda Cross Account access with Serverless framework

Details
Category: Code
By Stuart Mathews
Stuart Mathews
27.Feb
27 February 2019
Last Updated: 27 February 2019
Hits: 10310
  • AWS

If you find yourself with an issue that the API Gateway cannot invoke your lambda due to an access denied issue:

AccessDeniedException: User: arn:aws:sts::ACCOUNT_ID:assumed-role/xxxxxx-EDS-API-Gateway-TaskDef-use1-PreProduction/b8884262-4cdd-4ee1-84a7-7676d1d5b914 is not authorized to perform: lambda:InvokeFunction on resource: YOUR_LAMBDA_ARN status code: 403, request id: 3e61d932-968e-4d15-b84e-b3eb61f0a30b

You can add a permission to your Lambda that will allow cross acount access:

LambdaInvokePermissionPreProd:
      Type: AWS::Lambda::Permission
      Properties:
        Action: lambda:InvokeFunction
        FunctionName: YOUR_LAMBDA_ARN HERE
        Principal: AN_ACCOUNT_ID_HERE
    LambdaInvokePermissionProd:
      Type: AWS::Lambda::Permission
      Properties:
        Action: lambda:InvokeFunction
        FunctionName: YOUR_LAMBDA_ARN HERE
        Principal: ANOTHER_ACCOUNT_ID_HERE

This is actually adds a function policy to your lambda which controls who can access your lambda. This is not to be confused with the Lambda role in which the lambda executes, ie the execution policy which only controls what the lambda itself can or can not access.

Actually it might be easier to see the whole file, right?

# Welcome to Serverless!
#
# This file is the main config file for your service.
# It's very minimal at this point and uses default values.
# You can always add more config options for more control.
# We've included some commented out config examples here.
# Just uncomment any of them to get that config option.
#
# For full config options, check the docs:
#    docs.serverless.com
#
# Happy Coding!
# Trigger

service: MyService
package:
  individually: true


provider:
  name: aws
  region: \({file(config/config.yml):region}
  role: LambdaRole
  vpc:
    securityGroupIds:
      - Ref: LambdaSecurityGroup
    subnetIds: \){file(config/config.yml):subnetIds}
functions:
  NotifyPW:
    handler: Lambda::MyService.Functions::Get
    runtime: dotnetcore2.0
    package:
      artifact: 'artifact-Deploy.zip'
    events:
      - http:
          path: get
          method: get

    environment:
      ENVIRONMENT: '\({file(config/config.yml):shortenvironment}'
resources:
  Resources:
    LambdaRole:
      Type: AWS::IAM::Role
      Properties:
        RoleName: 204992-MyService-\){file(config/config.yml):shortenvironment}
        AssumeRolePolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Principal:
              Service: [lambda.amazonaws.com]
            Action: ['sts:AssumeRole']
        Path: /
        Policies:
        - PolicyName: 204992-MyService-\({file(config/config.yml):shortenvironment}-policy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
            - Action:
              - "sqs:*Queue*"
              - "sqs:*Message*"           
              Resource:
                - "arn:aws:sqs:us-east-1:*"
              Effect: Allow              
            - Action:
                - logs:CreateLogGroup
                - logs:CreateLogStream
                - logs:PutLogEvents
                - logs:DescribeLogStreams
              Resource:
                - arn:aws:logs:*:*:*
              Effect: Allow
            - Action:
              - "sqs:SendMessage"
              - "sqs:ReceiveMessage"
              Resource:
                - "arn:aws:sqs:us-east-1:*:*"
              Effect: Allow
            - Action:
                - 'ec2:CreateNetworkInterface'
                - 'ec2:DescribeNetworkInterfaces'
                - 'ec2:DeleteNetworkInterface'
              Resource: '*'
              Effect: Allow
            - Action:
                - 'events:*Rule*'
              Resource: '*'
              Effect: Allow
            - Action:
                - 'lambda:InvokeFunction'
              Resource: '*'
              Effect: Allow

    LambdaSecurityGroup:
      Type: "AWS::EC2::SecurityGroup"
      Properties:
        GroupDescription: '204992 MyService Security Group'
        VpcId: \){file(config/config.yml):vpcId}        
    LambdaInvokePermissionEdpPreProd:
      Type: AWS::Lambda::Permission
      Properties:
        Action: lambda:InvokeFunction
        FunctionName: YOUR_LAMBDA_ARN
        Principal: AN_ACCOUNT_ID_HERE
    LambdaInvokePermissionEdpProd:
      Type: AWS::Lambda::Permission
      Properties:
        Action: lambda:InvokeFunction
        FunctionName: YOUR_LAMBDA_ARN
        Principal: ANOTHER_ACCOUNT_ID_HERE

More Articles …

  1. Mostly copies of itself
  2. Bind behind the scenes
  3. Using Language Ext
  4. Pure functions
  5. Complex or Elaborate
  6. Nullable checking in the Wild-West
  7. Understanding how the Linq query syntax works
  8. Lazy loading
  9. Parameter type validation
  10. Risk mitigation, software design and API Integrations

Subcategories

Game Development Article Count:  28

I discovered the realms of game development purely by accident, having picked up a book entitled 'Core Techniques and Algorithms in Game Programming' and discovered a surprising niche of innovation in programming quite unparalleled to my day-to-day needs as a developer. Here optimisation, graphics rendering, and algorithms are used on a totally different level and its very interesting.

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Page 4 of 17