/projects/mixer-controller/img1.jpg

Mixer controller

Published @ 30-03-2022

#c# #arduino #winAPI #serial

Control surface for the windows volume mixer

After constantly alt-tabbing to adjust the volume of other programs, I deceided to make a phyisical controller to do that instead.

The project consists of 2 parts the software for the PC and the firmware for the controller. There is also a config.xml file in which the user can link the controls to a program.

Repo —> ✨Mixer-Controller

Hardware

The controller will constantly read out the fader, and send their value over serial to the PC.

The setup is pretty simple - an Arduino pro micro with 4 faders connected to its ADC pins. It will take an initial reading of all the potmeters positions and save that in an array. So we can later compare that to the newly read value to filter out some unwanted noise, and properly detect a change in their position.

— In hindsight, it was probably better to use a small capacitor on the ADC input pins to acomplish this.

When it has detected that the potmeter has moved, it sends out a string over the serial connection, in the following format.


Serial string

The String looks like this [type],[id],[value]


Field Description
[type] the type of control element, such as a button fader or switch, currently only fader is supported but future proofing and all that.
[id] which of the faders it is, currently the hardware only has 4 faders so it would be 0-3.
[value] for type = fader this would be the position value of the potmeter so 0-100.


Audio Fader/Potmeter Fix

The potmeters I used where scrapped from an old Behringer audio mixer, meaning they are logarithmic, which is a problem because their response will not be linear.

But no worries, after some reading online I found you can get a somewhat linear response from those potmeters.

By simply adding a 1.5k resistor between the wiper and one of the other terminals, and this bit of code to convert the potmeter’s ADC readout to linear. return int((10 * pow(input / 1023.,10)) * 10);


Software

This was the hard part. After doing some ✨big brain googling ✨ I came across a way to interact with the windows volume mixer in C# using the ISimpleAudioVolume object. I had never written C# before so this was a cool opportunity to dive into it.

Setting the levels?

There is this function SetMasterVolume() which allows you to set the volume level of a ISimpleAudioVolume object, you can get access to this object by calling GetVolumeObject() which takes the PID of the process you want to change the audio level of.

So in order for us to set the volume of for example Chrome; which has multiple processes running (one for each tab), we first have to find all the PIDs that are part of Chrome.

updateAudioObjectList()

— Getting the objects

This is the bit that looks for all the processes that have audio playing and thus have adjustable levels.

It calls Process.GetProcessesByName() which will return an array of all the matching Processes after that, it starts gathering all the processes that have an ISimpleAudioVolume object and stores them in a list.
Because this function will be called very rapidly when there is serial data incomming, I added a limit for it to only run in 5 second windows. This fixes a ton of cpu usage because of the scanning through all these processes.

It stores the Processes that have an audio object in appObjects, which is a keyvalue pair.

— We can later use this keyvalue pair to quickly get a list of all a program’s audio emitting processes by going chromeProcs[] = appObjects["chrome"].

Serial communication

The moment the program receives any serial data on the COMPORT the function literally named callback() will trigger allowing us the handle these events accordingly.

When called it will parse out the serial string into a DataFrame which is esstentially a struct containing the same fields that the serial string has. Then it will match the DataFrame.id to the correct application (set in the config.xml file) and set its volume using MixerController.setProgramVolume().

setProgramVolume()

This function will call updateAudioObjectList() to update the appObjects[] pair, after that it simply uses the keyvalue pair to find all the processes that need their volume changed and pass those PIDs to SetApplicationVolume()



Action shots

img img

© 2024 Joppe Boeve. All rights reserved.