DIYing a DSP Processor engine. (Solution) - AVS Forum | Home Theater Discussions And Reviews
Forum Jump: 
 18Likes
Reply
Thread Tools
post #1 of 192 Old 10-30-2016, 03:39 AM - Thread Starter
AVS Forum Special Member
 
BassThatHz's Avatar
 
Join Date: Apr 2008
Location: Northern Okan range (NW Cascades region)
Posts: 7,267
Mentioned: 74 Post(s)
Tagged: 0 Thread(s)
Quoted: 2050 Post(s)
Liked: 1587
DIYing a DSP Processor engine. (Solution)

So, after using JRiver for a while I was getting pretty fed up with it's jittery real-time DSP in ASIO mode (from pre-testing my Motu project initiative).

So... this led me on a quest to write my own DSP processor and ASIO driver in C# !!!

After some quick googling, I realized that there was a bunch of people that had already beat me to the concept by about +10 years. (SWEET! )

Not only that, but it was open-source. (SWEET! )
The open-source project is called: NAudio

These guys have already coded Biquads in C#, and a C#-native ASIO interface adapter, with PCM wave buffering included.

HOLY COW batman!

Now for the boring part (try to stay awake! )

Spoiler!


They have pretty much done 99% of what I need.
Look at this, it is perfect, EXACTLY WHAT I NEEDED!


Code:
 public void SetLowPassFilter(float sampleRate, float cutoffFrequency, float q)
        {
            // H(s) = 1 / (s^2 + s/Q + 1)
            var w0 = 2 * Math.PI * cutoffFrequency / sampleRate;
            var cosw0 = Math.Cos(w0);
            var alpha = Math.Sin(w0) / (2 * q);

            var b0 = (1 - cosw0) / 2;
            var b1 = 1 - cosw0;
            var b2 = (1 - cosw0) / 2;
            var aa0 = 1 + alpha;
            var aa1 = -2 * cosw0;
            var aa2 = 1 - alpha;
            SetCoefficients(aa0,aa1,aa2,b0,b1,b2);
        }

        /// <summary>
        /// Set this up as a peaking EQ
        /// </summary>
        /// <param name="sampleRate">Sample Rate</param>
        /// <param name="centreFrequency">Centre Frequency</param>
        /// <param name="q">Bandwidth (Q)</param>
        /// <param name="dbGain">Gain in decibels</param>
        public void SetPeakingEq(float sampleRate, float centreFrequency, float q, float dbGain)
        {
            // H(s) = (s^2 + s*(A/Q) + 1) / (s^2 + s/(A*Q) + 1)
            var w0 = 2 * Math.PI * centreFrequency / sampleRate;
            var cosw0 = Math.Cos(w0);
            var sinw0 = Math.Sin(w0);
            var alpha = sinw0 / (2 * q);
            var a = Math.Pow(10, dbGain / 40);     // TODO: should we square root this value?

            var b0 = 1 + alpha * a;
            var b1 = -2 * cosw0;
            var b2 = 1 - alpha * a;
            var aa0 = 1 + alpha / a;
            var aa1 = -2 * cosw0;
            var aa2 = 1 - alpha / a;
            SetCoefficients(aa0, aa1, aa2, b0, b1, b2);
        }

        /// <summary>
        /// Set this as a high pass filter
        /// </summary>
        public void SetHighPassFilter(float sampleRate, float cutoffFrequency, float q)
        {
            // H(s) = s^2 / (s^2 + s/Q + 1)
            var w0 = 2 * Math.PI * cutoffFrequency / sampleRate;
            var cosw0 = Math.Cos(w0);
            var alpha = Math.Sin(w0) / (2 * q);

            var b0 = (1 + cosw0) / 2;
            var b1 = -(1 + cosw0);
            var b2 = (1 + cosw0) / 2;
            var aa0 = 1 + alpha;
            var aa1 = -2 * cosw0;
            var aa2 = 1 - alpha;
            SetCoefficients(aa0, aa1, aa2, b0, b1, b2);
        }
Code:
ASIOError ASIOInit(ASIODriverInfo *info);
ASIOError ASIOExit(void);
ASIOError ASIOStart(void);
ASIOError ASIOStop(void);
ASIOError ASIOGetChannels(long *numInputChannels, long *numOutputChannels);
ASIOError ASIOGetLatencies(long *inputLatency, long *outputLatency);
ASIOError ASIOGetBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity);
ASIOError ASIOCanSampleRate(ASIOSampleRate sampleRate);
ASIOError ASIOGetSampleRate(ASIOSampleRate *currentRate);
ASIOError ASIOSetSampleRate(ASIOSampleRate sampleRate);
ASIOError ASIOGetClockSources(ASIOClockSource *clocks, long *numSources);
ASIOError ASIOSetClockSource(long reference);
ASIOError ASIOGetSamplePosition (ASIOSamples *sPos, ASIOTimeStamp *tStamp);
ASIOError ASIOGetChannelInfo(ASIOChannelInfo *info);
ASIOError ASIOCreateBuffers(ASIOBufferInfo *bufferInfos, long numChannels,long bufferSize, ASIOCallbacks *callbacks);
ASIOError ASIODisposeBuffers(void);
ASIOError ASIOControlPanel(void);
void *ASIOFuture(long selector, void *params);
ASIOError ASIOOutputReady(void);
So I took their EQ module and merged it with their ASIO module, and StreamReader PCM buffer module, and built this beast!

This is not the whole thing (just the interesting bit.)
Code:
 private void Play()
        {
            // allow change device
            if (this.asioOut != null && 
                (this.asioOut.DriverName != comboBoxAsioDevice.Text || 
                this.asioOut.ChannelOffset != GetUserSpecifiedChannelOffset()))
            {
                this.asioOut.Dispose();
                this.asioOut = null;
            }
            
            // create device if necessary
            if (this.asioOut == null)
            {
                this.asioOut = new AsioOut(comboBoxAsioDevice.Text);
                this.asioOut.ChannelOffset = GetUserSpecifiedChannelOffset();

                var bands = new EqualizerBand[]
                    {
                        new EqualizerBand {Bandwidth = 0.8f, Frequency = 25, Gain = 0},
                        new EqualizerBand {Bandwidth = 0.8f, Frequency = 25, Gain = 0}
                    };

                equalizer = new Equalizer(reader, bands);
                equalizer.Update();

                this.asioOut.Init(equalizer);
            }
            
            this.reader.Position = 0;
            this.asioOut.Play();
            this.timer1.Enabled = true;
            SetButtonStates();
        }

private void CreateFilters()
        {
            for (int bandIndex = 0; bandIndex < bandCount; bandIndex++)
            {
                var band = bands[bandIndex];
                for (int n = 0; n < channels; n++)
                {
                    if (filters[n, bandIndex] == null)
                        filters[n, bandIndex] = BiQuadFilter.HighShelf(sourceProvider.WaveFormat.SampleRate, 5, 2.0F, -20);
                            //BiQuadFilter.PeakingEQ(sourceProvider.WaveFormat.SampleRate, band.Frequency, band.Bandwidth, band.Gain);
                    //else
                        //filters[n, bandIndex].SetPeakingEq(sourceProvider.WaveFormat.SampleRate, band.Frequency, band.Bandwidth, band.Gain);
                }
            }
        }
and it worked!

Now I just have to connect the read buffers to the write buffers to enable real-time DSP'ing.

Then code my channel mappers and filters, and BAM. Done!

No more need for third-party bloatware at all. (All you need is Windows and .Net framework, and then it just works! No installation needed and FREE!)

Now I can have complete control of the DSP and have it process any number of channels in real-time at lightning speeds!
No more jitter and defects, no more hair pulling. Just pure audio. (YES )
Attached Thumbnails
Click image for larger version

Name:	6687698.jpg
Views:	1693
Size:	187.7 KB
ID:	1742697  
03roadking likes this.

Last edited by BassThatHz; 10-30-2016 at 01:46 PM.
BassThatHz is offline  
Sponsored Links
Advertisement
 
post #2 of 192 Old 10-30-2016, 03:39 AM - Thread Starter
AVS Forum Special Member
 
BassThatHz's Avatar
 
Join Date: Apr 2008
Location: Northern Okan range (NW Cascades region)
Posts: 7,267
Mentioned: 74 Post(s)
Tagged: 0 Thread(s)
Quoted: 2050 Post(s)
Liked: 1587
Alpha version 0.1 is done

I cleaned up their source code a lot, at least the demo app. Not the NAudio API.
I did import the NAudio.dll into the EXE so that it would be a single file download.

It is just a ASIO stream writer (i.e. a player) right now, but it has 1 NAudio PEQ, a HPF, and a LPF. Fully adjustable.

Right now I believe it is limited to 2 output channels.

.Net 4.0 is required
I was aiming for 3.5 but it wouldn't let me, I assume that is because of the volume slider I am using(?)

Be forewarned that this code has kernel-level hooks in it, so if you mistype text into the textboxes it could give you a BSOD, (that said, I didn't encounter any BSOD's during development.)

I've only tested it with wave files, but NAudio says mp3 should work too.


https://drive.google.com/file/d/0BwU...ew?usp=sharing

The next step is to get the ASIO stream reader to work. Then I have to attach the two buffers together, and then we will see what happens! (i.e. jitter or no jitter.)

Fingers crossed...

Oh and to those who think .Net is too slow for ASIO. Absolute nonsense.
Once you cut out the bloatware, it is plenty fast (thus far at least...)

That said, C++ and Assembly is a lot faster for such tasks, if you want to spend 6 months programming it. LOL
Attached Thumbnails
Click image for larger version

Name:	45678.jpg
Views:	1020
Size:	97.9 KB
ID:	1742921  

Last edited by BassThatHz; 10-30-2016 at 08:32 AM.
BassThatHz is offline  
post #3 of 192 Old 10-30-2016, 03:55 AM
Advanced Member
 
Join Date: Jan 2015
Posts: 819
Mentioned: 8 Post(s)
Tagged: 0 Thread(s)
Quoted: 464 Post(s)
Liked: 47
Quote:
Originally Posted by BassThatHz View Post
[reserved]
So what exactly will this software allow you to do? Does it handle a complete setup of Dsp options that will work on anyone's system so long as they have a sufficient PC and enough output channels?
Tip24/96 is offline  
 
post #4 of 192 Old 10-30-2016, 08:09 AM - Thread Starter
AVS Forum Special Member
 
BassThatHz's Avatar
 
Join Date: Apr 2008
Location: Northern Okan range (NW Cascades region)
Posts: 7,267
Mentioned: 74 Post(s)
Tagged: 0 Thread(s)
Quoted: 2050 Post(s)
Liked: 1587
Quote:
Originally Posted by Tip24/96 View Post
So what exactly will this software allow you to do? Does it handle a complete setup of Dsp options that will work on anyone's system so long as they have a sufficient PC and enough output channels?
Yes. Input and Output channels.

It will basically be a real-time ASIO input to output bridge/router, with a no-holds-barred DSP in the middle, with an "unlimited" number of channels and filters. (or as many as I could care to provide at least. )

It MUST support at least 32x64 channels, with at least 20 PEQ filters per channel, plus a HPF/LPF, a delay and a polarity inverter; as that is what I need...

I might throw in a limiter or compressor or gate, depending on how I feel about it.

Right now it is single threaded, but I'm almost sure I'll have to add multi-threading support at some point, as a single core will get easily overloaded by that much DSP, even an i7 I would think...

Last edited by BassThatHz; 10-30-2016 at 08:12 AM.
BassThatHz is offline  
post #5 of 192 Old 10-30-2016, 08:16 AM - Thread Starter
AVS Forum Special Member
 
BassThatHz's Avatar
 
Join Date: Apr 2008
Location: Northern Okan range (NW Cascades region)
Posts: 7,267
Mentioned: 74 Post(s)
Tagged: 0 Thread(s)
Quoted: 2050 Post(s)
Liked: 1587
Oh and whoever invented the BiQuad algorithm, is a flipping genius!

It takes like no CPU horsepower to run them at all. It seems pretty fast if you ask me; but what do I know...
BassThatHz is offline  
post #6 of 192 Old 10-30-2016, 08:35 AM
Senior Member
 
ja00's Avatar
 
Join Date: May 2013
Posts: 408
Mentioned: 20 Post(s)
Tagged: 0 Thread(s)
Quoted: 199 Post(s)
Liked: 133
Am I understanding it correctly when I say this is the ASIO version of Equalizer APO?
ja00 is online now  
post #7 of 192 Old 10-30-2016, 08:57 AM
AVS Forum Special Member
 
PretzelFisch's Avatar
 
Join Date: Feb 2011
Posts: 1,101
Mentioned: 6 Post(s)
Tagged: 0 Thread(s)
Quoted: 400 Post(s)
Liked: 262
You don't think you'll see issues when the GC starts?
andyc56 likes this.
PretzelFisch is online now  
post #8 of 192 Old 10-30-2016, 03:14 PM - Thread Starter
AVS Forum Special Member
 
BassThatHz's Avatar
 
Join Date: Apr 2008
Location: Northern Okan range (NW Cascades region)
Posts: 7,267
Mentioned: 74 Post(s)
Tagged: 0 Thread(s)
Quoted: 2050 Post(s)
Liked: 1587
Just working on version 0.2

Multi ASIO devices, real-time processing, Input bus sends, Mixer/Filter buses, and Output bus receives.
I got the first 8 channels on the screen; I need to build some custom scrollable controls to make this smaller. But for now, this will do...

I will keep the file player there for debugging purposes, and for those that don't have multi-client ASIO drivers nor two ASIO devices.

A lot of programs don't support two ASIO devices. I can understand why:
ASIO drivers require single-threaded application windows, and it locks onto the window with a C++ pointer.
If you try to open two ASIO drivers from the same app it will prevent you from doing this, or possibly even crash.

In order to get around this I think I might have to spin up 3 windows (and thus: 3 threads), and manage the communication between the window's threads myself.
I haven't tried it yet, but it doesn't seem like this would be a big deal (in my opinion), I've done multi-window apps and multi-threaded apps before.
I'm up for the challenge...

I love not having to wait for developers to add and release new features with their slow cycles, spanning years.
I can add what ever I want, when ever I want. It feels good. LOL
Attached Thumbnails
Click image for larger version

Name:	5678.png
Views:	967
Size:	257.3 KB
ID:	1743809  

Last edited by BassThatHz; 10-31-2016 at 06:57 PM.
BassThatHz is offline  
post #9 of 192 Old 10-30-2016, 03:23 PM - Thread Starter
AVS Forum Special Member
 
BassThatHz's Avatar
 
Join Date: Apr 2008
Location: Northern Okan range (NW Cascades region)
Posts: 7,267
Mentioned: 74 Post(s)
Tagged: 0 Thread(s)
Quoted: 2050 Post(s)
Liked: 1587
Quote:
Originally Posted by ja00 View Post
Am I understanding it correctly when I say this is the ASIO version of Equalizer APO?
Yep, pretty much...

That said, I don't think I'll be making the GUI and feature set "that fancy" though.

I'm more interested it raw speed than a fancy GUI, loading the CPU cores with a bunch of unnecessary pixels, FFT's and VST plugin's etc etc etc.

I'm trying to keep it as lean and as mean as possible, at least for version 1...
artsci2 likes this.
BassThatHz is offline  
post #10 of 192 Old 10-30-2016, 03:32 PM
Member
 
Join Date: Apr 2016
Posts: 39
Mentioned: 0 Post(s)
Tagged: 0 Thread(s)
Quoted: 21 Post(s)
Liked: 0
Quote:
Originally Posted by BassThatHz View Post
Yep, pretty much...

That said, I don't think I'll be making the GUI and feature set "that fancy" though.

I'm more interested it raw speed than a fancy GUI, loading the CPU cores with a bunch of unnecessary pixels, FFT's and VST plugin's etc etc etc.

I'm trying to keep it as lean and as mean as possible, at least for version 1...
Will this work with for example the creative sound blaster omi 5.1? I dont have a DSP and my subwoofer is in a sealed box flopping around at the infrasonics.

Verstuurd vanaf mijn SM-G935F met Tapatalk
Luke98 is offline  
post #11 of 192 Old 10-30-2016, 04:48 PM - Thread Starter
AVS Forum Special Member
 
BassThatHz's Avatar
 
Join Date: Apr 2008
Location: Northern Okan range (NW Cascades region)
Posts: 7,267
Mentioned: 74 Post(s)
Tagged: 0 Thread(s)
Quoted: 2050 Post(s)
Liked: 1587
Quote:
Originally Posted by PretzelFisch View Post
You don't think you'll see issues when the GC starts?
No worries, I use Visual Studio 2013 Professional edition, which has a memory and CPU profiler.
I can see exactly which lines of code are being naughty or nice.



Apparently Microsoft's windows app message pump is consuming the most memory.
The windows app's main constructor being the second most naughty.
Between the two we are talking about 12,000 variables and ~1MB of ram. So who cares...


The "show files" dialog is using the most CPU horsepower by far... 37% of the total usage. LOL!

The file reader function, the most disk I/O (obviously...)

But here are the two most-important functions of all, the ASIO audio-loop function and the DSP function (these do all the heavy audio work):

Attached Thumbnails
Click image for larger version

Name:	678955.jpg
Views:	942
Size:	137.6 KB
ID:	1744049   Click image for larger version

Name:	678964.jpg
Views:	954
Size:	124.6 KB
ID:	1744057   Click image for larger version

Name:	8980.jpg
Views:	949
Size:	131.0 KB
ID:	1744065   Click image for larger version

Name:	65789.jpg
Views:	92
Size:	357.3 KB
ID:	1744073   Click image for larger version

Name:	546789.png
Views:	92
Size:	38.6 KB
ID:	1744081  

Click image for larger version

Name:	456789.jpg
Views:	101
Size:	273.1 KB
ID:	1744089   Click image for larger version

Name:	4673.jpg
Views:	952
Size:	314.3 KB
ID:	1744097   Click image for larger version

Name:	456733.jpg
Views:	949
Size:	107.4 KB
ID:	1744137  
neo_2009 likes this.

Last edited by BassThatHz; 10-30-2016 at 04:56 PM.
BassThatHz is offline  
post #12 of 192 Old 10-30-2016, 05:16 PM - Thread Starter
AVS Forum Special Member
 
BassThatHz's Avatar
 
Join Date: Apr 2008
Location: Northern Okan range (NW Cascades region)
Posts: 7,267
Mentioned: 74 Post(s)
Tagged: 0 Thread(s)
Quoted: 2050 Post(s)
Liked: 1587
There, I just sped up the DSP engine by 11% by removing the temporary memory allocations. Making them fixed at the class level outside of the DSP loop.
I know that's non-standard practise, but this isn't business code, I'm optimizing for speed, because speed is the most important thing!

I see that the variable I'm using to signal the bands checkbox on and off is a rather expensive operation, both the check and the variable.
I should probably make the filters as always-on. It would speed up the DSP by another ~6%. Good to know!


For now the NAudio ASIO code seems fast so I'd rather not go poking my nose around there trying to optimize their code. (But I will if I have to, like say: once I load up a 16x32 channel I/O stream, if it gets jittery... etc)
Attached Thumbnails
Click image for larger version

Name:	5678.jpg
Views:	963
Size:	130.2 KB
ID:	1744265  

Last edited by BassThatHz; 10-30-2016 at 05:19 PM.
BassThatHz is offline  
post #13 of 192 Old 10-30-2016, 05:40 PM - Thread Starter
AVS Forum Special Member
 
BassThatHz's Avatar
 
Join Date: Apr 2008
Location: Northern Okan range (NW Cascades region)
Posts: 7,267
Mentioned: 74 Post(s)
Tagged: 0 Thread(s)
Quoted: 2050 Post(s)
Liked: 1587
With the enabled filter check removed, the BiQuads become the limiting factor. It is slower than the buffer reader.
Which is good, that's what we want! (Well, sort of. LOL!)


As a result of this analysis I have decided to create an ultra fast mode option in the GUI:
This forces the filters to be always-on.


When you unselect it, it will do the check, which will slow the DSP down. At least this way I am giving the user a choice.
I could see that being handy for config/setup reasons.

Once you've nailed down all your filters and routing, click fast mode to reduce latency.
Attached Thumbnails
Click image for larger version

Name:	9879768.png
Views:	949
Size:	39.3 KB
ID:	1744321   Click image for larger version

Name:	54657.jpg
Views:	945
Size:	82.9 KB
ID:	1744329  

Last edited by BassThatHz; 10-30-2016 at 05:48 PM.
BassThatHz is offline  
post #14 of 192 Old 10-30-2016, 06:02 PM
Senior Member
 
sandydankness's Avatar
 
Join Date: Oct 2013
Location: Green Bay
Posts: 373
Mentioned: 4 Post(s)
Tagged: 0 Thread(s)
Quoted: 108 Post(s)
Liked: 77
I just want to congratulate you on the first thread I have read on this forum where I understand nothing! Finally, I get to tune out instead of add another project!
Peter M and Oval56 like this.
sandydankness is offline  
post #15 of 192 Old 10-30-2016, 09:27 PM
Senior Member
 
ja00's Avatar
 
Join Date: May 2013
Posts: 408
Mentioned: 20 Post(s)
Tagged: 0 Thread(s)
Quoted: 199 Post(s)
Liked: 133
Quote:
Originally Posted by BassThatHz View Post
Yep, pretty much...

That said, I don't think I'll be making the GUI and feature set "that fancy" though.

I'm more interested it raw speed than a fancy GUI, loading the CPU cores with a bunch of unnecessary pixels, FFT's and VST plugin's etc etc etc.

I'm trying to keep it as lean and as mean as possible, at least for version 1...
If you are planning on releasing this for mass usage, I would request some sort of integration with REW. It could be as simple as saving an EQ file in a folder that will be read and applied.
ja00 is online now  
post #16 of 192 Old 10-30-2016, 11:26 PM
AVS Forum Special Member
 
Augerhandle's Avatar
 
Join Date: Jan 2009
Location: About 25" away from my computer screen
Posts: 4,430
Mentioned: 15 Post(s)
Tagged: 0 Thread(s)
Quoted: 758 Post(s)
Liked: 658
BassThatHz likes this.

"The wise understand by themselves; fools follow the reports of others"-Tibetan Proverb
_____________________ http://www.scientificamerican.com/article/auger-handle/ ____________________________
Augerhandle is offline  
post #17 of 192 Old 10-31-2016, 12:14 AM
Member
 
awediophile's Avatar
 
Join Date: Oct 2013
Posts: 181
Mentioned: 4 Post(s)
Tagged: 0 Thread(s)
Quoted: 128 Post(s)
Liked: 51
Quote:
Originally Posted by BassThatHz View Post
There, I just sped up the DSP engine by 11% by removing the temporary memory allocations. Making them fixed at the class level outside of the DSP loop.
I know that's non-standard practise, but this isn't business code, I'm optimizing for speed, because speed is the most important thing!
Actually, this is not just "standard" practice for realtime code, it is essential practice. A general purpose memory allocator almost always has an indeterminate and potentially long execution time. In the worst case, the allocation request could be forced to wait on the OS to swap out memory, which could take hundreds of milliseconds or more.

When writing realtime code, speed in the usual sense is a secondary requirement. Bounded low latency is your *first* priority. By bounded, I mean, you know exactly how long it will take in the worst case and you make the effort to minimize that worst case. The reason for this is that there is a fixed interval within which all your processing *must* complete, or your software will fail to deliver audio data to the interface in time. The consequence is a transfer under-run (or XRUN, which is short for either transfer under-run or over-run) and an audio drop-out. While audio drop-outs are not usually hazardous, they can be very annoying and are undesirable, even when they occur only occasionally.

I strongly recommend you do some research into realtime audio programming and realtime programming in general. The subject is advanced and specialized. Good knowledge is in very short supply. Much of what you you know about writing business software does not apply here at all.

The C# language may not be suitable for this work for a number of reasons. I won't say it's impossible (see notes here), in part because I have limited understanding of the language and its run-time environment, but if you don't know any better, there is a risk of spending a lot of time on code that can't be made to work. You might get to the point where it's 99% OK but still has XRUNs frequently enough to drive you nuts. My own language of choice for "normal" code, Python, is definitely *not* suitable. Garbage collection in general is a big no-no for realtime. With C#, you will have to find a way to turn it off or prevent it from being activated at the very least.

Save the high level languages for the user interface and write the core code in a low level language to run in a dedicated realtime thread or even process. I prefer straight C over C++ for this purpose as most of the OO stuff just gets in the way and adds nasty overhead. Low latency audio processing involves much smaller buffers than are typically used for general purpose computing or for video or 3D. Overhead counts for a lot! Also don't forget that the modern CPU cache is also a potential point of indeterminacy.

With multiple realtime threads involved, things get even crazier. Thread synchronization can contribute a lot of overhead and is a new potential source of indeterminacy, depending on the implementation. CPU cache contention between cores can have unpredictable effects. Another problem that arises whenever multiple threads with different priorities must synchronize is priority inversion.

Anyway, this is just scratching the surface of the subject. A lot depends on how much latency you are willing to accept and how reliable you want it be. If you are willing to tolerate a lot of latency and/or occasional XRUNs, you can probably get something to work in C# or whatever without bothering with too many details. Even Python has a library that allows one to write Jack applications with a realtime process callback. (Note: Jack is a realtime audio library). It works well enough for a demo / proof of concept / toy, but it's flaky and finicky. How would you feel about a preamp or audio processor that had random audio drop-outs and/or periodically crashed? Do you want something that performs like a toy or like a piece of professional equipment?
awediophile is online now  
post #18 of 192 Old 10-31-2016, 07:12 PM - Thread Starter
AVS Forum Special Member
 
BassThatHz's Avatar
 
Join Date: Apr 2008
Location: Northern Okan range (NW Cascades region)
Posts: 7,267
Mentioned: 74 Post(s)
Tagged: 0 Thread(s)
Quoted: 2050 Post(s)
Liked: 1587
Quote:
Originally Posted by Luke98 View Post
Will this work with for example the creative sound blaster omi 5.1? I dont have a DSP and my subwoofer is in a sealed box flopping around at the infrasonics.
I'm not familiar with that soundcard, however with a virtual audio multi-client ASIO driver like JackRouter, I don't see why it wouldn't be possible.
Have fun pulling your hair trying to get JackRouter to do the connections you want it to in Windows.

Quote:
Originally Posted by ja00 View Post
If you are planning on releasing this for mass usage, I would request some sort of integration with REW. It could be as simple as saving an EQ file in a folder that will be read and applied.
Now you are just adding more work for me.

I've never looked at REW's EQ export file before, no idea how complicated that would be...
BassThatHz is offline  
post #19 of 192 Old 10-31-2016, 07:29 PM
AVS Forum Special Member
 
PretzelFisch's Avatar
 
Join Date: Feb 2011
Posts: 1,101
Mentioned: 6 Post(s)
Tagged: 0 Thread(s)
Quoted: 400 Post(s)
Liked: 262
Quote:
Originally Posted by BassThatHz View Post
Now you are just adding more work for me.

I've never looked at REW's EQ export file before, no idea how complicated that would be...
Well if you open source it on github or bitbucket then you might not have to do it all.
PretzelFisch is online now  
post #20 of 192 Old 10-31-2016, 09:30 PM - Thread Starter
AVS Forum Special Member
 
BassThatHz's Avatar
 
Join Date: Apr 2008
Location: Northern Okan range (NW Cascades region)
Posts: 7,267
Mentioned: 74 Post(s)
Tagged: 0 Thread(s)
Quoted: 2050 Post(s)
Liked: 1587
I was able to connect multiple ASIO devices via the same thread. (As far as I can tell...)

No XRUNS.
It is definitely registering on a Multi-client ASIO driver as 2 readers and 2 writers on the same window and thread.



Two writer sources to the Oppo also works perfectly via Jack Router. No XRUNS


I'm getting jitter in real-time mode. No XRUNS. Jack Router is sending the data to the Oppo as a WDM connection (I assume that is the problem...), it must be dropping bits or rounding numbers. Maybe LSB/MSB issue. I can hear the music though...
"I don't think" it's JRiver...
Same ASIO device as reader and writer at once from the same thread.


The problem is that when I step up Jack Router to 32 inputs and outputs, the real-time part stops working (mute).
I think I might have to throw out their code and write the DSP handler class myself, I'm pulling out my hair trying to make their sample code do what I want (it's not well written), written by a noob it seems. I already knew that the moment I saw the code.


I also tried to lower the buffer size, but Jack Router wouldn't let me do it.
I'll test it with a direct-Oppo ASIO connection and see if NAudio can do it or not.

More testing is needed.

Last edited by BassThatHz; 10-31-2016 at 09:33 PM.
BassThatHz is offline  
post #21 of 192 Old 10-31-2016, 09:49 PM - Thread Starter
AVS Forum Special Member
 
BassThatHz's Avatar
 
Join Date: Apr 2008
Location: Northern Okan range (NW Cascades region)
Posts: 7,267
Mentioned: 74 Post(s)
Tagged: 0 Thread(s)
Quoted: 2050 Post(s)
Liked: 1587
I wanted to see how high I could crank the channel counts.
It connected with no XRUNS at 256 channels in and 256 channels out.

If you need more channels than that, then you have a serious health issue. LOL
I have a 4K screen and I can't even fit them all visibly on it.

If you are wondering what the limit of my buses and filters are. 4 billion. Each... They are 32bit indexes.
Something tells me you'll run out of RAM and CPU power before the internal array limit is reached. LOL
But something tells me you'll run out of patience clicking add filter, before even that happens LOL!
(Problem solved then...? )
Attached Thumbnails
Click image for larger version

Name:	11.jpg
Views:	832
Size:	238.7 KB
ID:	1746609  

Last edited by BassThatHz; 10-31-2016 at 10:43 PM.
BassThatHz is offline  
post #22 of 192 Old 10-31-2016, 10:13 PM - Thread Starter
AVS Forum Special Member
 
BassThatHz's Avatar
 
Join Date: Apr 2008
Location: Northern Okan range (NW Cascades region)
Posts: 7,267
Mentioned: 74 Post(s)
Tagged: 0 Thread(s)
Quoted: 2050 Post(s)
Liked: 1587
It's a Jack Router problem.

I was able to set the Buffer size to 64, and min Lat in Oppo control panel.
Foobar won't even run in this mode.

My app kept up without a hiccup.



The Oppo doesn't have a faster setting than this.
Attached Thumbnails
Click image for larger version

Name:	54.jpg
Views:	819
Size:	127.3 KB
ID:	1746641   Click image for larger version

Name:	8785.jpg
Views:	822
Size:	180.6 KB
ID:	1746649  
BassThatHz is offline  
post #23 of 192 Old 10-31-2016, 10:36 PM - Thread Starter
AVS Forum Special Member
 
BassThatHz's Avatar
 
Join Date: Apr 2008
Location: Northern Okan range (NW Cascades region)
Posts: 7,267
Mentioned: 74 Post(s)
Tagged: 0 Thread(s)
Quoted: 2050 Post(s)
Liked: 1587
The ASIO control panel for my Xonar has a 1ms option. So I set it to that.



Its ASIO driver reports it as 48, with a min buffer size of 4.
So I forced it to use a buffer size of 4, and it still worked.
But I did have all DSP disabled...and this was in player-mode. Not real-time I/O mode.

So honestly I'm not sure what my apps "top speed" is in player-mode. (FAST is the answer...)
It does things that Foobar can't (it doesn't let you set the buffer lower than 50ms), SOX resampler doesn't like small buffers.

JRiver allowed me to set the Xonar to 1ms in the driver, and the buffer to 5ms or min-hardware, and it worked too. (Although I can't confirm if JRiver is actually applying such settings or just faking it. LOL)
Attached Thumbnails
Click image for larger version

Name:	677.jpg
Views:	819
Size:	126.6 KB
ID:	1746673   Click image for larger version

Name:	654.jpg
Views:	822
Size:	24.5 KB
ID:	1746681  

Last edited by BassThatHz; 11-08-2016 at 01:04 AM.
BassThatHz is offline  
post #24 of 192 Old 11-01-2016, 12:44 AM - Thread Starter
AVS Forum Special Member
 
BassThatHz's Avatar
 
Join Date: Apr 2008
Location: Northern Okan range (NW Cascades region)
Posts: 7,267
Mentioned: 74 Post(s)
Tagged: 0 Thread(s)
Quoted: 2050 Post(s)
Liked: 1587
The jitter went away when I rebooted this morning. I think I was hammering my audio drivers so hard yesterday that it messed them up. LOL
It seems to have fixed itself.

Anyways here is version 0.2

To put it into real-time mode you first have to be playing a wave file, that's because of that noob's code I was talking about that needs replacing.

I have a hidden Easter Egg, when the app receives an input stream in real-time mode it will change the windows text to the read buffer's bytes. It will be non-zero, this way you know it is getting a signal. I will be removing it, it's just for debug purposes.

Right now I have the DSP disabled until I get a chance to re-write that dudes code. Is what it is...

I believe it is limited to 2 inputs and 2 outputs, and 16 bit 44.1k for the moment.
https://drive.google.com/open?id=0Bw...1lfN21kcjFOQXc

I have it showing the soundcards recommended buffer. Right now it is hardcoded to the recommended setting.


The output master volume is coupled to the inputs volume for the time being.
Attached Thumbnails
Click image for larger version

Name:	6543.jpg
Views:	798
Size:	110.7 KB
ID:	1746857  

Last edited by BassThatHz; 11-02-2016 at 01:12 AM.
BassThatHz is offline  
post #25 of 192 Old 11-01-2016, 01:54 AM - Thread Starter
AVS Forum Special Member
 
BassThatHz's Avatar
 
Join Date: Apr 2008
Location: Northern Okan range (NW Cascades region)
Posts: 7,267
Mentioned: 74 Post(s)
Tagged: 0 Thread(s)
Quoted: 2050 Post(s)
Liked: 1587
Quote:
Originally Posted by awediophile View Post
Actually, this is not just "standard" practice for realtime code, it is essential practice. A general purpose memory allocator almost always has an indeterminate and potentially long execution time. In the worst case, the allocation request could be forced to wait on the OS to swap out memory, which could take hundreds of milliseconds or more.

When writing realtime code, speed in the usual sense is a secondary requirement. Bounded low latency is your *first* priority. By bounded, I mean, you know exactly how long it will take in the worst case and you make the effort to minimize that worst case. The reason for this is that there is a fixed interval within which all your processing *must* complete, or your software will fail to deliver audio data to the interface in time. The consequence is a transfer under-run (or XRUN, which is short for either transfer under-run or over-run) and an audio drop-out. While audio drop-outs are not usually hazardous, they can be very annoying and are undesirable, even when they occur only occasionally.

I strongly recommend you do some research into realtime audio programming and realtime programming in general. The subject is advanced and specialized. Good knowledge is in very short supply. Much of what you you know about writing business software does not apply here at all.

The C# language may not be suitable for this work for a number of reasons. I won't say it's impossible (see notes here), in part because I have limited understanding of the language and its run-time environment, but if you don't know any better, there is a risk of spending a lot of time on code that can't be made to work. You might get to the point where it's 99% OK but still has XRUNs frequently enough to drive you nuts. My own language of choice for "normal" code, Python, is definitely *not* suitable. Garbage collection in general is a big no-no for realtime. With C#, you will have to find a way to turn it off or prevent it from being activated at the very least.

Save the high level languages for the user interface and write the core code in a low level language to run in a dedicated realtime thread or even process. I prefer straight C over C++ for this purpose as most of the OO stuff just gets in the way and adds nasty overhead. Low latency audio processing involves much smaller buffers than are typically used for general purpose computing or for video or 3D. Overhead counts for a lot! Also don't forget that the modern CPU cache is also a potential point of indeterminacy.

With multiple realtime threads involved, things get even crazier. Thread synchronization can contribute a lot of overhead and is a new potential source of indeterminacy, depending on the implementation. CPU cache contention between cores can have unpredictable effects. Another problem that arises whenever multiple threads with different priorities must synchronize is priority inversion.

Anyway, this is just scratching the surface of the subject. A lot depends on how much latency you are willing to accept and how reliable you want it be. If you are willing to tolerate a lot of latency and/or occasional XRUNs, you can probably get something to work in C# or whatever without bothering with too many details. Even Python has a library that allows one to write Jack applications with a realtime process callback. (Note: Jack is a realtime audio library). It works well enough for a demo / proof of concept / toy, but it's flaky and finicky. How would you feel about a preamp or audio processor that had random audio drop-outs and/or periodically crashed? Do you want something that performs like a toy or like a piece of professional equipment?
I generally agree. However I have never ran into a issue with .Net's GC. I've been using .Net since version 1.0, some 16 years ago now.

I believe it only cleans up stuff that doesn't have an active pointer. The longer the memory is allocated the less frequently the GC scans them. It puts it in a sort of "don't touch" place, excluded from the more frequently churned objects.
ASP.Net and IIS is a different beast entirely, but on winforms once you allocate the memory it usually just "steps aside", as long as you aren't creating and destroying memory assignments during each buffer request then I don't think it will be a problem at all.

In C# you can actually code in C++ directly at any time with an unmanaged code directive, so that is always a possibility. That is EXACTLY how the NAudio ASIO code attaches, it drops into C++ mode and then back into C# mode once it has the function pointers and memory references loaded.

In c# there are 3 multi-threaded options: manual and semi-auto and full-auto.
The 2 auto's will never produce a threading issue as microsoft has coded it in a way that avoids it.
In manual mode you can get yourself into trouble if one isn't careful, but it is often the fastest mode IF you can keep the cores busy enough for the switching and sync'ing to not cause a huge performance hit.
There is usually no benefit to multi-threading until you have first maxed out the resources of the first core to 100% (ignoring non-heavy things that would be better suited for a simple asynchronous callback, such as network IO and disk IO etc.)

I'm pretty sure I'll be able to make it do what I want. I already got the first 2 real-time IO channel bridges working.
Even if I have to spin up 24 instances of this app, it would probably still be better than a single instance of JRiver for what I'm doing. Since it is limited to 32 channels and mine isn't (crosses fingers), and mine runs in direct ASIO mode, not some sort of slow VST model + bloatware.

I pretty much grab the input buffer's memory pointer, do a single pass on it that populates the requested output buffer's memory pointer address with the BiQuads applied.
It doesn't get more efficient than that. It's "almost" a direct kernel-mode hardware-to-hardware bridge.

Actually I think the NAudio's core is doing some other stuff that slows it down by 1 extra go-between buffer, but I'll re-code the NAudio core and remove that extra step if slows me down in any way.
But no sense optimizing stuff to avoid a problem that doesn't "yet exist". Rewriting that NAudio core would be a fair bit of tedious work... something I'm trying to avoid and reserve as an absolute last resort.
BassThatHz is offline  
post #26 of 192 Old 11-01-2016, 03:08 AM
Member
 
awediophile's Avatar
 
Join Date: Oct 2013
Posts: 181
Mentioned: 4 Post(s)
Tagged: 0 Thread(s)
Quoted: 128 Post(s)
Liked: 51
With the C++ mode and the fact that you can mostly avoid triggering GC, you have a good shot at making it work fairly well. Your best bet would probably still be to re-write any code that gets run during any actual processing in C++ and have it executed in its own thread or threads. Automatic multi-threading is probably a terrible idea here. You'll need to use manual threading and you'll need to work out solutions to the communication and synchronization problems. Realize that for realtime processing, you need CPU headroom to avoid XRUNs. A single core is probably good to about 50-70% utilization. Multiple cores probably need more headroom than that, but it really depends on a lot of details.

What do you mean when you say you are sometimes hearing jitter but are not getting XRUNs? Can you go into more detail about how it sounds? Your description of it sounding like lost bits and/or a byte order is puzzling. If the audio were being down-converted to a lower bit-depth, the quantization noise floor would increase, leading to a kind of blurring of fine details, but this would unlikely be especially noticeably until bit depth got below at least 16 bits. (It's probably more like 12-14 bits.) OTOH, if there was a mismatch in bit depth, byte order, or sign convention between the application and device, the result will be excruciatingly distorted, possibly beyond all recognition. (This is a great way to fry equipment and/or blow out ear drums if you don't have any attenuation between the processor and the amps and they are turned on.)

This last point should be obvious but still deserves mention. If you are running the DSP connected to amps without any attenuation in between, then any software glitch that corrupts the audio buffers can have hazardous consequences, depending on the system and equipment. Do you know what continuous white noise sounds like? IMO, it's quite annoying at 65 dB RMS average. Want to find out what continuous white noise at 145 dB RMS average sounds like? I sure don't.
awediophile is online now  
post #27 of 192 Old 11-01-2016, 04:08 PM
AVS Forum Special Member
 
DreamWarrior's Avatar
 
Join Date: Nov 2005
Posts: 1,174
Mentioned: 13 Post(s)
Tagged: 0 Thread(s)
Quoted: 339 Post(s)
Liked: 250
Cool little project -- at one point I was thinking about leveraging Nvidia's CUDA platform to compute multi-channel FIR filters without burdening the CPU. I figured it'd allow more channels and taps at higher sample rates than the CPU could handle given the GPU's propensity for high FLOPs.

In fact, I thought it'd be really cool to be able to use an HDMI capture card (with a HDFury to strip the protection) and then turn the PC into an all-digital processor. Of course, that brings up the lack of any non-payware decoders for Dolby TrueHD / DTS MA (and none for Atmos / DTS-X) that I know of, so...bit-streaming and object audio is out of the question at the moment.

Alas, I have no time to experiment. But, maybe if you get this going in any sort of capacity I'll be inclined to muck around, lol.
DreamWarrior is offline  
post #28 of 192 Old 11-01-2016, 07:14 PM - Thread Starter
AVS Forum Special Member
 
BassThatHz's Avatar
 
Join Date: Apr 2008
Location: Northern Okan range (NW Cascades region)
Posts: 7,267
Mentioned: 74 Post(s)
Tagged: 0 Thread(s)
Quoted: 2050 Post(s)
Liked: 1587
Quote:
Originally Posted by awediophile View Post
Automatic multi-threading is probably a terrible idea here. You'll need to use manual threading and you'll need to work out solutions to the communication and synchronization problems. Realize that for realtime processing, you need CPU headroom to avoid XRUNs. A single core is probably good to about 50-70% utilization. Multiple cores probably need more headroom than that, but it really depends on a lot of details.
I agree. I was thinking of assigning a thread for each output channel.
That way as intel releases 64 core'd CPU's the software would still scale fairly well, even if you had a lot of channels going.

Reading a read-only array across many threads isn't a big deal in c# in manual mode. It's only when you want to write/transfer data across threads or shared memory that you get into big trouble, so by having each thread write to its own output buffer and handle it's own DSP, that appears to make sense.

Doing it any other way would probably be disastrous from a real-time perspective.

I could have c# monitor the CPU usage and once it hits 40% I'd switch into multi-threaded coded to spread the load out across the cores. That would be fairly easy to do as C# allows you to loop through the available hyper-threads and re-assign itself however you want (from 1 all the way to hardware-max.)

Beyond which the OS just "does its magic" assigning and/or time-splicing as it sees fit. (Like it always does.)

I believe the ASIO output buffer is actually interleaved between the channels, so literally ever X'th element in the array would be written to by the X'th thread. I don't think this would be a problem, if I recall correctly from the past, writing like that doesn't cause any problems as the other threads aren't trying to write or read to those memory locations, at all.
(Just don't EVER change the array size without first stopping the audio stream and all the threads. )

It would probably be very noticeable if there was a multi-threading bug because the audio stream would either stop or produce white-noise. So as long as that doesn't happen then you know that you have a working solution.

My Oppo has it's own chip-level volume control, so I'm able to tone down the noise during development of experimental code.
I believe the Motu's have a similar thing so long as you are at 96khz or less. Above 96 motu's DSP is disabled which I would imagine includes disabling any volume control (I don't know yet as I don't have the hardware yet.)

Without a Motu box I'm limited to JackRouter for testing beyond 2-ch at the moment. It appears to work, but it's not as good as Motu for this task.

Last edited by BassThatHz; 11-01-2016 at 07:43 PM.
BassThatHz is offline  
post #29 of 192 Old 11-01-2016, 07:17 PM - Thread Starter
AVS Forum Special Member
 
BassThatHz's Avatar
 
Join Date: Apr 2008
Location: Northern Okan range (NW Cascades region)
Posts: 7,267
Mentioned: 74 Post(s)
Tagged: 0 Thread(s)
Quoted: 2050 Post(s)
Liked: 1587
Quote:
Originally Posted by awediophile View Post
Can you go into more detail about how it sounds?
I sounded like a CD with scratches on it, not enough to stop or skip, but enough to sound bad. Like tiny clicks every 50ms.
Once I rebooted the problem went away and hasn't (yet) returned.
BassThatHz is offline  
post #30 of 192 Old 11-01-2016, 07:18 PM
AVS Forum Special Member
 
notnyt's Avatar
 
Join Date: Dec 2008
Location: Long Island, NY
Posts: 8,012
Mentioned: 183 Post(s)
Tagged: 0 Thread(s)
Quoted: 2258 Post(s)
Liked: 2056
Quote:
Originally Posted by awediophile View Post
Save the high level languages for the user interface and write the core code in a low level language to run in a dedicated realtime thread or even process. I prefer straight C over C++ for this purpose as most of the OO stuff just gets in the way and adds nasty overhead. Low latency audio processing involves much smaller buffers than are typically used for general purpose computing or for video or 3D. Overhead counts for a lot! Also don't forget that the modern CPU cache is also a potential point of indeterminacy.
+1 for C for RT
notnyt is offline  
Sponsored Links
Advertisement
 
Reply DIY Speakers and Subs



Forum Jump: 

Posting Rules  
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are Off
Pingbacks are Off
Refbacks are Off