<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://weblogs.asp.net/utility/FeedStylesheets/atom.xsl" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><title type="html">A Portion of Buff</title><subtitle type="html">Everybody else had one, so...</subtitle><id>http://weblogs.asp.net/jarnold/atom.aspx</id><link rel="alternate" type="text/html" href="http://weblogs.asp.net/jarnold/default.aspx" /><link rel="self" type="application/atom+xml" href="http://weblogs.asp.net/jarnold/atom.aspx" /><generator uri="http://communityserver.org" version="3.0.20510.895">Community Server</generator><updated>2004-07-07T17:07:00Z</updated><entry><title>Noise 0.3 released</title><link rel="alternate" type="text/html" href="http://weblogs.asp.net/jarnold/archive/2005/04/12/399904.aspx" /><id>http://weblogs.asp.net/jarnold/archive/2005/04/12/399904.aspx</id><published>2005-04-11T23:31:00Z</published><updated>2005-04-11T23:31:00Z</updated><content type="html">&lt;font face="Verdana" size="2"&gt;I've put a new cut of Noise, the premier solution for C# VST developers (ahem), up on &lt;a href="http://gforge.public.thoughtworks.org/projects/noise/"&gt;GForge&lt;/a&gt;.&amp;nbsp; The changelog:&lt;br /&gt; &lt;br /&gt; * Finished implementing v1.0 of the SDK _apart from_ setChunk()/getChunk() because I don't know how best to handle them yet.&amp;nbsp; It would&lt;br /&gt; be nice to do something a bit smarter than a byte stream.&amp;nbsp; Suggestions welcome.&lt;br /&gt; &lt;br /&gt; * Refactored ManagedBridge a little bit so it directly returns values instead of using out params.&lt;br /&gt; &lt;br /&gt; * Added very basic error handling to HostPlugin - if your plugin fails to initialise properly, it will report nicely back to the host.&lt;br /&gt; &lt;br /&gt; * Added an example of a compressor.&amp;nbsp; Doesn't work properly yet, but it _sort of_ sounds compressor-like.&lt;br /&gt; &lt;br /&gt; * You can now provide your own unique ID to the host (see CompressorPlugin for an example).&lt;br /&gt; &lt;br /&gt; * Made some of the process classes mono, which makes them a bit easier to understand.&lt;/font&gt;&lt;br /&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=399904" width="1" height="1"&gt;</content><author><name>jimski3000</name><uri>http://weblogs.asp.net/members/jimski3000.aspx</uri></author></entry><entry><title>The subdued garbage collector</title><link rel="alternate" type="text/html" href="http://weblogs.asp.net/jarnold/archive/2005/04/06/397289.aspx" /><id>http://weblogs.asp.net/jarnold/archive/2005/04/06/397289.aspx</id><published>2005-04-05T23:41:00Z</published><updated>2005-04-05T23:41:00Z</updated><content type="html">&lt;font face="Verdana" size="2"&gt;I want &lt;a href="http://gforge.public.thoughtworks.org/projects/noise"&gt;Noise&lt;/a&gt; to support any .Net language, not just C# (although why anyone would use another language...).&amp;nbsp; At the moment, the plugin interface exposes unsafe methods - Process and ProcessReplacing - which take float* parameters and are therefore unusable by a pussy language like VB.Net.&amp;nbsp; Pointers are used because it allows us to write directly to the host's memory instead of allocating our own buffers and copying them back to the host.&lt;br /&gt; &lt;br /&gt; If we are prepared to take a performance hit, we can make this interface safe.&amp;nbsp; Not only is there the cost of copying unmanaged memory to a managed buffer on the way in, and then back again on the way out, we need to allocate some managed memory in the first place.&amp;nbsp; Allocations lead to collections, collections lead to hate, hate leads to suffering.&lt;br /&gt; &lt;br /&gt; However, we can take advantage of the fact that the maximum number of samples being passed to the plugin rarely changes (usually not until the host's latency setting has been changed, which is hardly ever under normal use), although the host is free to send fewer samples if it wishes.&amp;nbsp; The host tells us the maximum block size by calling the SetBlockSize() method, which is a good place to allocate our buffers:&lt;/font&gt;&lt;br /&gt; &lt;br /&gt; &lt;font face="Courier New" size="2"&gt;public override void SetBlockSize(int blockSize)&lt;br /&gt; {&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; leftBufferIn = new float[blockSize];&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; rightBufferIn = new float[blockSize];&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; leftBufferOut = new float[blockSize];&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; rightBufferOut = new float[blockSize];&lt;br /&gt; }&lt;/font&gt;&lt;br /&gt; &lt;br /&gt; &lt;font face="Verdana" size="2"&gt;and in the Process or ProcessReplacing methods, we can just fill these buffers using the Marshal class:&lt;/font&gt;&lt;br /&gt; &lt;br /&gt; &lt;font face="Courier New" size="2"&gt;public override void ProcessReplacing(float* inLeft, float* inRight, float* outLeft, float* outRight, int sampleFrames)&lt;br /&gt; {&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; Marshal.Copy(new IntPtr(inLeft), leftBufferIn, 0, sampleFrames);&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; Marshal.Copy(new IntPtr(inRight), rightBufferIn, 0, sampleFrames);&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; Marshal.Copy(new IntPtr(outLeft), leftBufferOut, 0, sampleFrames);&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; Marshal.Copy(new IntPtr(outRight), rightBufferOut, 0, sampleFrames);&lt;br /&gt; //...&lt;br /&gt; }&lt;/font&gt;&lt;br /&gt; &lt;br /&gt; &lt;font face="Verdana" size="2"&gt;It doesn't matter if sampleFrames is less than blockSize, we just copy that many samples to our buffers.&amp;nbsp; This enables us to call out to a safe method which deals with float arrays, and performance is constant because no new allocations occur.&amp;nbsp; Hurrah.&lt;/font&gt;&lt;br /&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=397289" width="1" height="1"&gt;</content><author><name>jimski3000</name><uri>http://weblogs.asp.net/members/jimski3000.aspx</uri></author></entry><entry><title>Noise 0.2 source released</title><link rel="alternate" type="text/html" href="http://weblogs.asp.net/jarnold/archive/2005/03/25/395887.aspx" /><id>http://weblogs.asp.net/jarnold/archive/2005/03/25/395887.aspx</id><published>2005-03-25T14:58:00Z</published><updated>2005-03-25T14:58:00Z</updated><content type="html">&lt;font face="Verdana" size="2"&gt;Now installs to the GAC (this was the only way I could let hosts load a plugin from an arbitrary location.&amp;nbsp; Sometimes assembly-resolution sucks).&amp;nbsp; Added a reverb plugin to the package, and generally fixed up the solution.&amp;nbsp; To get up and running:&lt;br /&gt; &lt;br /&gt; 1) &lt;a href="http://gforge.public.thoughtworks.org/projects/noise"&gt;Download&lt;/a&gt; and extract noise-source-0.2.zip somewhere.&lt;br /&gt; 2) Download the VST Plugin SDK from &lt;a href="http://www.steinberg.net/Steinberg/Developers.asp"&gt;http://www.steinberg.net/Steinberg/Developers.asp&lt;/a&gt;&lt;br /&gt; 3) Copy everything from vstsdk2.3\source\common into Noise\HostPlugin\vst&lt;br /&gt; 4) Build the solution&lt;br /&gt; 5) Open project properties for Noise.ManagedVst, go to Configuration Properties--&amp;gt;Debugging&lt;br /&gt; 6) Change Debug Mode to "Program"&lt;br /&gt; 7) Change "Start Application" to point to VstHost.exe (under Noise\ by default)&lt;br /&gt; 8) Hit [ctrl +] F5&lt;br /&gt; 9) Load up your favourite synth plugin, then chain HostPlugin.dll after it (deployed in Noise\build).&lt;br /&gt; 10) Enjoy&lt;/font&gt;&lt;font face="Verdana" size="2"&gt;&lt;br /&gt; &lt;/font&gt;&lt;br /&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=395887" width="1" height="1"&gt;</content><author><name>jimski3000</name><uri>http://weblogs.asp.net/members/jimski3000.aspx</uri></author></entry><entry><title>On microbenchmarks and red faces</title><link rel="alternate" type="text/html" href="http://weblogs.asp.net/jarnold/archive/2005/03/23/395634.aspx" /><id>http://weblogs.asp.net/jarnold/archive/2005/03/23/395634.aspx</id><published>2005-03-23T13:49:00Z</published><updated>2005-03-23T13:49:00Z</updated><content type="html">&lt;p&gt;&lt;font face="Verdana" size="2"&gt;Dear &lt;a href="http://blog.signaleleven.com/index.php?itemid=12"&gt;VB.Net H8t3R&lt;/a&gt;,&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;Try making a single call to Strings.Trim(s) before you start benchmarking.&amp;nbsp; I think a static constructor just bit you on the ass.&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;Love,&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;Your nemesis&lt;/font&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=395634" width="1" height="1"&gt;</content><author><name>jimski3000</name><uri>http://weblogs.asp.net/members/jimski3000.aspx</uri></author></entry><entry><title>Keeping it real-time (3)</title><link rel="alternate" type="text/html" href="http://weblogs.asp.net/jarnold/archive/2005/03/22/395423.aspx" /><id>http://weblogs.asp.net/jarnold/archive/2005/03/22/395423.aspx</id><published>2005-03-22T00:03:00Z</published><updated>2005-03-22T00:03:00Z</updated><content type="html">&lt;font face="Verdana" size="2"&gt;These late nights are going to catch up with me, but until then...&lt;br /&gt; &lt;br /&gt; I've kicked off &lt;a href="http://gforge.public.thoughtworks.org/projects/noise/"&gt;Noise&lt;/a&gt;, an open source project for this managed VST plugin thing I'm working on:&lt;br /&gt; &lt;a href="http://gforge.public.thoughtworks.org/projects/noise/"&gt;&lt;/a&gt;&lt;a href="https://gforge.public.thoughtworks.org/projects/noise/"&gt;&lt;/a&gt;&lt;br /&gt; (Last year I was working on an abortive software synthesizer called "Noise!", so it seemed appropriate to recycle the name).&lt;br /&gt; &lt;br /&gt; It's obviously quite rough at the moment, but please, if you use any VST-compatible audio software (Cubase, SoundForge, FruityLoops etc), download it, give it a try, and let me know if you had any problems (positive feedback is nice too).&amp;nbsp; If you don't have a VST host, there's a free one included, so you can still play around if you're interested.&amp;nbsp; &lt;br /&gt; &lt;br /&gt; So far I've got delay, lo/hi-pass filter, gain and reverb plug-ins - the latter being the most fun to do and the most difficult to get my head around.&amp;nbsp; One thing that struck me tonight was just how incredibly fast computers are.&amp;nbsp; I know I should be accustomed to that, being a developer, but when you're writing code which applies 12 different filters to 44,100 stereo samples per second, in parallel to a software synthesizer which is generating those 44,100 stereo samples per second with its own oscillators, filters and envelopes, on top of a host application routing all these samples from the synth, through your plugin (via layers of C, Managed C++ and C#) and out to a sound card, and it only takes 3% of available CPU time, it kind of brings it home.&lt;br /&gt; &lt;br /&gt; Here's a stereo comb filter:&lt;/font&gt;&lt;br /&gt; &lt;br /&gt; &lt;font face="Courier New" size="2"&gt;public class CombFilter&lt;br /&gt; {&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; float feedback;&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; float damp1;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; float damp2;&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; float lastLeftValue;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; float lastRightValue;&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; int leftIndex;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; int rightIndex;&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; float[] leftBuffer;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; float[] rightBuffer;&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; public CombFilter(int leftTuning, int rightTuning, float damp, float feedback)&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; this.feedback = feedback;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; this.damp1 = damp;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; this.damp2 = (1 - damp);&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; this.leftBuffer = new float[leftTuning];&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; this.rightBuffer = new float[rightTuning];&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; public float ProcessLeft(float input)&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; lastLeftValue = (leftBuffer[leftIndex] * damp2) + (lastLeftValue * damp1);&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; leftBuffer[leftIndex] = input + (lastLeftValue * feedback);&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if (++leftIndex &amp;gt;= leftBuffer.Length)&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; leftIndex = 0;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return leftBuffer[leftIndex];&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; public float ProcessRight(float input)&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; lastRightValue = (rightBuffer[rightIndex] * damp2) + (lastRightValue * damp1);&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; rightBuffer[rightIndex] = input + (lastRightValue * feedback);&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if (++rightIndex &amp;gt;= rightBuffer.Length)&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; rightIndex = 0;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return rightBuffer[rightIndex];&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt; }&lt;/font&gt;&lt;br /&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=395423" width="1" height="1"&gt;</content><author><name>jimski3000</name><uri>http://weblogs.asp.net/members/jimski3000.aspx</uri></author></entry><entry><title>Keeping it real-time (2)</title><link rel="alternate" type="text/html" href="http://weblogs.asp.net/jarnold/archive/2005/03/16/394764.aspx" /><id>http://weblogs.asp.net/jarnold/archive/2005/03/16/394764.aspx</id><published>2005-03-15T23:31:00Z</published><updated>2005-03-15T23:31:00Z</updated><content type="html">&lt;font face="Verdana" size="2"&gt;I had a request for more (more!!!), so I'll explain how some more of the managed plugin system works.&amp;nbsp; At the root of it, we have the VST SDK-derived class.&amp;nbsp; This is a standard C++ class which inherits from AudioEffectX (defined as part of the SDK).&amp;nbsp; Any VST host will expect an instance of this type, and will call certain methods to both interrogate your plugin about its capabilities and pass it the magical float** inputs and outputs.&amp;nbsp; (I remember reading somewhere about three star C programmers, and how they were a breed unto themselves.&amp;nbsp; We only have two stars here, but as .Net programmers, I think we should allow ourselves a smug grin).&lt;br /&gt; &lt;br /&gt; As Managed C++ classes cannot inherit from unmanaged classes, our root plugin must be unmanaged.&amp;nbsp; However, unmanaged classes can have references to managed classes, via the gcroot&amp;lt;&amp;gt; template (there may be other ways - I have not discovered them yet).&amp;nbsp; A VST host will typically want to know how many inputs and outputs a plugin can handle, whether it can be a parallel ("send") or serial ("insert") effect, whether it has its own GUI for editing parameters, etc.&amp;nbsp; My goal is to delegate as much as possible to C#, so most of these calls must be marshalled directly to managed classes.&amp;nbsp; The good news is that MC++ makes this marshalling really fricken easy.&amp;nbsp; Anyone who's done much P/Invoke will know how fiddly all those MarshalAs attributes can be to get right.&amp;nbsp; Well, most of the time, with MC++, It Just Works.&amp;nbsp; You have to make a few decisions about which side owns which piece of memory, but I'm 90% convinced that doing interop from the unmanaged side is just easier.&amp;nbsp; Although maybe that's just the pointers talking.&amp;nbsp; An example:&lt;/font&gt;&lt;br /&gt; &lt;br /&gt; &lt;font face="Courier New" size="2"&gt;void VstToManaged::process (float **inputs, float **outputs, long sampleFrames)&lt;br /&gt; {&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; m_pPluginBridge-&amp;gt;Process(inputs, outputs, sampleFrames);&lt;br /&gt; }&lt;/font&gt;&lt;br /&gt; &lt;br /&gt; &lt;font face="Verdana" size="2"&gt;That takes us from an unmanaged, VST 0wn3d method to MC++.&amp;nbsp; No mucking about with DllImport, just include the header file.&lt;br /&gt; &lt;br /&gt; Did I just say that?&amp;nbsp; &lt;/font&gt;&lt;br /&gt; &lt;font face="Verdana" size="2"&gt;&lt;br /&gt; Anyway, taking the process() method further, I decided that most C# weenies wouldn't know what to do with two levels of indirection, so I dereferenced the left and right channels and created an interface thusly:&lt;/font&gt;&lt;br /&gt; &lt;br /&gt; &lt;font face="Courier New" size="2"&gt;public interface IVstPlugin&lt;br /&gt; {&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; unsafe void Process(float* inLeft, float* inRight, float* outLeft, float* outRight, int sampleFrames);&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; &lt;font face="Arial"&gt;&lt;font face="Verdana"&gt;There's more on this interface than that, but I'll keep it simple for now.&amp;nbsp; The code from an MC++ point of view?&lt;/font&gt;&lt;br /&gt; &lt;br /&gt; &lt;font face="Courier New"&gt;void ManagedBridge::Process(float **inputs, float **outputs, long sampleFrames)&lt;br /&gt; {&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; float *in1&amp;nbsp; =&amp;nbsp; inputs[0];&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; float *in2&amp;nbsp; =&amp;nbsp; inputs[1];&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; float *out1 = outputs[0];&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; float *out2 = outputs[1];&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; plugin-&amp;gt;Process(in1, in2, out1, out2, sampleFrames);&lt;br /&gt; }&lt;br /&gt; &lt;font face="Verdana"&gt;&lt;br /&gt; Interop schminterop.&amp;nbsp; And, as I'm feeling philanthropic, here's a mono Biquad filter in C#.&amp;nbsp; Feed it a stream of samples and see what comes out:&lt;br /&gt; &lt;br /&gt; &lt;font face="Courier New"&gt;using System;&lt;br /&gt; namespace ManagedVst.Processes&lt;br /&gt; {&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; public enum FilterType : byte&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; LPF,&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; HPF,&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; //Ported and adapted from Tom St Denis' C version (http://www.musicdsp.org/showone.php?id=64)&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; public struct BiQuad&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; //Coefficients.&amp;nbsp; Don't ask me what they do.&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; double A0, A1, A2, A3, A4;&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; //X1 is the input from the previous step, X2 is the input before that&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; //Y1 is the output from the previous step, Y2 is the output before that&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; double X1, X2, Y1, Y2;&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; public void Init(FilterType filterType, double sampleRate, double frequency, double bandwidth)&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; //A frequency of 0 can lead to the filter producing&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //very small (denormal) numbers and NaNs.&amp;nbsp; This is bad because&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; //most processors are very slow at calculating very small floating point numbers, so we should&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; //clamp such values to something larger.&amp;nbsp; This might be slightly superstitious, but including&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; //the following check banished 100% CPU usage spikes.&amp;nbsp; Alternatives welcome (double.Epsilon didn't&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New" size="2"&gt;&lt;font face="Arial"&gt;&lt;font face="Courier New"&gt;&lt;font face="Verdana"&gt;&lt;font face="Courier New"&gt;appear &lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New" size="2"&gt;&lt;font face="Arial"&gt;&lt;font face="Courier New"&gt;&lt;font face="Verdana"&gt;&lt;font face="Courier New"&gt;to work).&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if(frequency == 0d)&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; frequency = 0.00000000000001d;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; const double LogE2 = 0.69314718055994530942;&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; double omega = 2 * Math.PI * frequency / sampleRate;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; double sinOmega = Math.Sin(omega);&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; double cosOmega = Math.Cos(omega);&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; double alpha = sinOmega * Math.Sinh(LogE2 / 2 * bandwidth * omega / sinOmega);&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; double a0 = 1 + alpha;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; double a1 = -2 * cosOmega;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; double a2 = 1 - alpha;&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; double b0 = 0;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; double b1 = 0;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; double b2 = 0;&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if (filterType == FilterType.LPF)&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; b0 = (1 - cosOmega) / 2;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; b1 = 1 - cosOmega;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; b2 = (1 - cosOmega) / 2;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; else //filterType == FilterType.HPF&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; b0 = (1 + cosOmega) / 2;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; b1 = -(1 + cosOmega);&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; b2 = (1 + cosOmega) / 2;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; A0 = b0 / a0;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; A1 = b1 / a0;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; A2 = b2 / a0;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; A3 = a1 / a0;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; A4 = a2 / a0;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; public float Step(float input)&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; double output = A0 * input + A1 * X1 + A2 * X2 - A3 * Y1 - A4 * Y2;&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; X2 = X1;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; X1 = input;&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; Y2 = Y1;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; Y1 = output;&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return (float)output;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt; }&lt;/font&gt;&lt;br /&gt; &lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=394764" width="1" height="1"&gt;</content><author><name>jimski3000</name><uri>http://weblogs.asp.net/members/jimski3000.aspx</uri></author></entry><entry><title>Keeping it real-time</title><link rel="alternate" type="text/html" href="http://weblogs.asp.net/jarnold/archive/2005/03/15/394605.aspx" /><id>http://weblogs.asp.net/jarnold/archive/2005/03/15/394605.aspx</id><published>2005-03-15T00:06:00Z</published><updated>2005-03-15T00:06:00Z</updated><content type="html">&lt;p class="MsoNormal"&gt;&lt;span style="font-size: 10pt; font-family: Verdana;"&gt;Since we &lt;A href="http://weblogs.asp.net/jarnold/archive/2005/02/21/377445.aspx"&gt;last&lt;/a&gt; spoke, I have realised that a managed VST plug-in is a far from practical application for hosting the CLR.&lt;span style=""&gt;&amp;nbsp; &lt;/span&gt;Which means no more COM haxx0ring.&lt;span style=""&gt;&amp;nbsp; &lt;/span&gt;Shame.&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal"&gt;&lt;span style="font-size: 10pt; font-family: Verdana;"&gt;On the upside, I have discovered the twin joys of Managed C++ and porting DSP code from C to C#.&lt;span style=""&gt;&amp;nbsp; &lt;/span&gt;MC++, after several failed attempts, now shuttles calls from the VST host through to C#.&lt;span style=""&gt;&amp;nbsp; &lt;/span&gt;The CLR is of course loaded automatically whenever the managed DLL is touched, so hosting it explicitly is pointless.&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal"&gt;&lt;span style="font-size: 10pt; font-family: Verdana;"&gt;It is splendid fun.&lt;span style=""&gt;&amp;nbsp; &lt;/span&gt;At the moment, much of the real-time processing is done with unsafe code (the VST host passes in pointers to the input and output buffers), but it’s nicer than working in C.&lt;span style=""&gt;&amp;nbsp; &lt;/span&gt;Here’s a simple delay:&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;; color: blue;"&gt;public&lt;/span&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;;"&gt; &lt;span style="color: blue;"&gt;class&lt;/span&gt; Delay&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; {&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;float&lt;/span&gt; samplingRate;&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;float&lt;/span&gt;[] delayLine;&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;int&lt;/span&gt; delayLineIndex;&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;int&lt;/span&gt; bufferLength;&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;private&lt;/span&gt; &lt;span style="color: blue;"&gt;float&lt;/span&gt; delayTime;&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;private&lt;/span&gt; &lt;span style="color: blue;"&gt;float&lt;/span&gt; feedback;&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;private&lt;/span&gt; &lt;span style="color: blue;"&gt;float&lt;/span&gt; dryLevel;&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;private&lt;/span&gt; &lt;span style="color: blue;"&gt;float&lt;/span&gt; wetLevel;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;private&lt;/span&gt; &lt;span style="color: blue;"&gt;const&lt;/span&gt; &lt;span style="color: blue;"&gt;int&lt;/span&gt; MAX_DELAY_TIME = 1000;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;public&lt;/span&gt; Delay(&lt;span style="color: blue;"&gt;float&lt;/span&gt; delayTime, &lt;span style="color: blue;"&gt;float&lt;/span&gt; feedBack, &lt;span style="color: blue;"&gt;float&lt;/span&gt; dryLevel, &lt;span style="color: blue;"&gt;float&lt;/span&gt; wetLevel, &lt;span style="color: blue;"&gt;float&lt;/span&gt; samplingRate)&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;{&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;this&lt;/span&gt;.delayTime = delayTime;&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;this&lt;/span&gt;.feedback = feedBack;&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;this&lt;/span&gt;.dryLevel = dryLevel;&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;this&lt;/span&gt;.wetLevel = wetLevel;&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;this&lt;/span&gt;.samplingRate = samplingRate;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;int&lt;/span&gt; maxBufferLength = CalculateBufferLength(MAX_DELAY_TIME);&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;delayLine = &lt;span style="color: blue;"&gt;new&lt;/span&gt; &lt;span style="color: blue;"&gt;float&lt;/span&gt;[maxBufferLength];&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;bufferLength = CalculateBufferLength(delayTime);&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color="#0000ff"&gt;this&lt;/font&gt;.&lt;/span&gt;delayLineIndex = 0;&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;}&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;;"&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;unsafe&lt;/span&gt; &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; Process(&lt;span style="color: blue;"&gt;float&lt;/span&gt;* input, &lt;span style="color: blue;"&gt;float&lt;/span&gt;* output, &lt;span style="color: blue;"&gt;int&lt;/span&gt; sampleFrames)&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;{&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;for&lt;/span&gt; (&lt;span style="color: blue;"&gt;int&lt;/span&gt; i = 0; i &amp;lt; sampleFrames; i++) &lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;{&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;output[i] += (dryLevel * input[i]) + (wetLevel * delayLine[delayLineIndex]);&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;delayLine[delayLineIndex] = input[i] + (feedback * delayLine[delayLineIndex]);&lt;o:p&gt;&lt;br /&gt; &lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;;"&gt;&lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;if&lt;/span&gt; (++delayLineIndex &amp;gt;= bufferLength)&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;{&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;delayLineIndex = 0;&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;}&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;}&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;}&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;;"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;float&lt;/span&gt; DelayTime&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; &lt;/span&gt;{&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;get&lt;/span&gt; { &lt;span style="color: blue;"&gt;return&lt;/span&gt; delayTime; }&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;set&lt;/span&gt; &lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;{&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;this&lt;/span&gt;.delayTime = &lt;span style="color: blue;"&gt;value&lt;/span&gt;;&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;this&lt;/span&gt;.bufferLength = CalculateBufferLength(&lt;span style="color: blue;"&gt;value&lt;/span&gt;);&lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;}&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;}&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal"&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;;"&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;float&lt;/span&gt; Feedback&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;{&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;get&lt;/span&gt; { &lt;span style="color: blue;"&gt;return&lt;/span&gt; feedback; }&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;set&lt;/span&gt; { feedback = &lt;span style="color: blue;"&gt;value&lt;/span&gt;; }&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;}&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;float&lt;/span&gt; DryLevel&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;{&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;get&lt;/span&gt; { &lt;span style="color: blue;"&gt;return&lt;/span&gt; dryLevel; }&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;set&lt;/span&gt; { dryLevel = &lt;span style="color: blue;"&gt;value&lt;/span&gt;; }&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;}&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;float&lt;/span&gt; WetLevel&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;{&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;get&lt;/span&gt; { &lt;span style="color: blue;"&gt;return&lt;/span&gt; wetLevel; }&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;set&lt;/span&gt; { wetLevel = &lt;span style="color: blue;"&gt;value&lt;/span&gt;; }&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;}&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;private&lt;/span&gt; &lt;span style="color: blue;"&gt;int&lt;/span&gt; CalculateBufferLength(&lt;span style="color: blue;"&gt;float&lt;/span&gt; delayTime)&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;{&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue;"&gt;return&lt;/span&gt; (&lt;span style="color: blue;"&gt;int&lt;/span&gt;)(delayTime * samplingRate / 1000);&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; &lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;}&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt; }&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal"&gt;&lt;span style="font-size: 10pt; font-family: Verdana;"&gt;Performance hasn’t been an issue so far, but I haven’t tried to do anything particularly taxing.&lt;span style=""&gt;&amp;nbsp; &lt;/span&gt;The real test will come when I start creating garbage, although I think Gen 0 collections will be tolerable.&lt;/span&gt;&lt;span style="font-family: Verdana;"&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=394605" width="1" height="1"&gt;</content><author><name>jimski3000</name><uri>http://weblogs.asp.net/members/jimski3000.aspx</uri></author></entry><entry><title>Practical applications for CLR hosting</title><link rel="alternate" type="text/html" href="http://weblogs.asp.net/jarnold/archive/2005/02/21/377445.aspx" /><id>http://weblogs.asp.net/jarnold/archive/2005/02/21/377445.aspx</id><published>2005-02-21T18:05:00Z</published><updated>2005-02-21T18:05:00Z</updated><content type="html">&lt;p class="MsoNormal"&gt;&lt;span style="font-size: 10pt; font-family: Verdana;"&gt;Well then.&amp;nbsp; It's been a long time since I last &lt;a href="../archive/2003/03/03/3347.aspx"&gt;got cozy&lt;/a&gt; with the unmanaged side of the CLR, but all the interop I have been doing lately has made me cocky.&amp;nbsp; This post is a write-up of my attempts to develop a VST plugin in C#.&lt;br /&gt; &lt;br /&gt; A &lt;i&gt;what&lt;/i&gt;?&lt;br /&gt; &lt;br /&gt; VST stands for &lt;/span&gt;&lt;span style="font-size: 10pt; font-family: Verdana;"&gt;Virtual Studio Technology.&amp;nbsp; It's a standard developed by &lt;a href="http://www.steinberg.de/"&gt;Steinberg&lt;/a&gt; for producing audio software components.&amp;nbsp; The idea is that a VST host application (Cubase, Logic Audio, ACID etc) acts as a software mixing desk, routing audio in and out of these plugins.&amp;nbsp; A plugin can be an effect, such as reverb or chorus, which changes the sound fed into them in some way; or an instrument which produces sounds of its own.&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal"&gt;&lt;span style="font-size: 10pt; font-family: Verdana;"&gt;Steinberg provides an SDK which developers can use to make plugins but it only provides a C++ API.&lt;span style=""&gt;&amp;nbsp; &lt;/span&gt;This is understandable as plugins need to be fast and work in realtime, and any long pauses could cause glitches in an audio stream or throw timing off.&lt;span style=""&gt;&amp;nbsp; &lt;/span&gt;In addition, C++ is the de facto standard for writing audio software.&lt;span style=""&gt;&amp;nbsp; &lt;/span&gt;On the other hand, as we have seen with Managed DirectX, it is possible to write performance-sensitive software in managed code as long as an effort is made to reduce the impact of garbage-collection and other overhead.&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal"&gt;&lt;span style="font-size: 10pt; font-family: Verdana;"&gt;VST plugins are loaded into the host application as native DLLs, so I needed some way to call out into managed code from that point.&lt;span style=""&gt;&amp;nbsp; &lt;/span&gt;There are a few options, but I took the route of hosting the CLR and calling into managed code with COM interop: partly because it sounds cool, and partly because it was the first thing that worked.&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal"&gt;&lt;span style="font-size: 10pt; font-family: Verdana;"&gt;I’m not going to explain how to use the VST SDK – there are plenty of tutorials out there (&lt;a href="http://code.glowdot.com/tutorials/vsttutorial.php"&gt;this&lt;/a&gt; is an excellent one).&lt;span style=""&gt;&amp;nbsp; &lt;/span&gt;This post is about hosting the CLR and calling into managed code, and assumes you can stumble your way through C++ COM code.&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal"&gt;&lt;span style="font-size: 10pt; font-family: Verdana;"&gt;Plugins inherit from a base class provided in the SDK called &lt;/span&gt;&lt;span style="font-size: 10pt; font-family: Verdana;"&gt;AudioEffectX. The constructor would seem to be an appropriate place to start up the CLR, because it is called before any performance-critical processing takes place.&lt;span style=""&gt;&amp;nbsp; &lt;/span&gt;The code to do this is well-documented (&lt;a href="http://www.gotdotnet.com/team/clr/HostingInterfaces.aspx"&gt;here&lt;/a&gt;, for starters), but looks something like this:&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;;"&gt;ICorRuntimeHost *pRuntimeHost = NULL;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;;"&gt;CorBindToRuntimeEx(&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;;"&gt;&lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;NULL, &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;;"&gt;&lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;NULL,&lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;;"&gt;&lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;0, &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;;"&gt;&lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;CLSID_CorRuntimeHost, &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;;"&gt;&lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;IID_ICorRuntimeHost, &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;;"&gt;&lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style=""&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;(LPVOID*) &amp;amp;pRuntimeHost);&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;;"&gt;pRuntimeHost-&amp;gt;Start();&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal"&gt;&lt;span style="font-size: 10pt; font-family: Verdana;"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;br /&gt; On its own, this won’t get us very far.&lt;span style=""&gt;&amp;nbsp; &lt;/span&gt;We need some managed code to run!&lt;span style=""&gt;&amp;nbsp; &lt;/span&gt;Getting a reference to the default AppDomain is easy enough:&lt;o:p&gt;&lt;br /&gt; &amp;nbsp;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;font face="Courier New"&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;;"&gt;IUnknown&lt;span style=""&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;*punk = NULL;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/font&gt;&lt;/p&gt; &lt;p class="MsoNormal"&gt;&lt;span style="font-size: 10pt; font-family: Verdana;"&gt;&lt;font face="Courier New"&gt;pRuntimeHost-&amp;gt;GetDefaultDomain(&amp;amp;punk);&lt;/font&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;;"&gt;_AppDomain *pDefaultDomain = NULL;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;;"&gt;punk-&amp;gt;QueryInterface(&lt;span style="color: blue;"&gt;__uuidof&lt;/span&gt;(_AppDomain), (PVOID*) &amp;amp;pDefaultDomain);&lt;o:p&gt;&lt;br /&gt; &amp;nbsp;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;span style="font-size: 10pt; font-family: Verdana;"&gt;The _AppDomain interface is, essentially, identical to the managed AppDomain class which means we use it to load assemblies, create objects, create new AppDomains and all that good stuff.&lt;span style=""&gt;&amp;nbsp; &lt;/span&gt;My managed code is in an assembly called “ManagedVst”, and I want to create an object of type “PluginAdapter” in the “ManagedVst” namespace:&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;;"&gt;_ObjectHandle *pObjHandle = NULL;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;;"&gt;pDefaultDomain-&amp;gt;CreateInstance(_bstr_t("ManagedVst"), _bstr_t("ManagedVst.PluginAdapter"), &amp;amp;pObjHandle); &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;;"&gt;&lt;font face="Verdana"&gt;Now we need to do some tedious unwrapping of all the COM layers around this object.&lt;span style=""&gt;&amp;nbsp; &lt;/span&gt;Eventually we end up with a pointer to an interface matching that of the managed class (I defined this interface explicitly, but you can get the framework to create one for you).&lt;/font&gt;&lt;o:p&gt;&lt;font face="Verdana"&gt;&lt;br /&gt; &lt;/font&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;;"&gt;VARIANT v;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;;"&gt;VariantInit(&amp;amp;v);&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;;"&gt;pObjHandle-&amp;gt;Unwrap(&amp;amp;v);&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;;"&gt;IPluginAdapter *pPluginAdapter;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;span style="font-size: 10pt; font-family: &amp;quot;Courier New&amp;quot;;"&gt;v.pdispVal-&amp;gt;QueryInterface(&lt;span style="color: blue;"&gt;__uuidof&lt;/span&gt;(IPluginAdapter), (&lt;span style="color: blue;"&gt;void&lt;/span&gt;**) &amp;amp;pPluginAdapter);&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="MsoNormal" style=""&gt;&lt;span style="font-size: 10pt; font-family: Verdana;"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;br /&gt; (Now you see why I want to write this plugin in C#?)&lt;span style=""&gt;&amp;nbsp; &lt;/span&gt;So, finally, we have a pathway to a custom managed object which we can use and abuse at will.&lt;span style=""&gt;&amp;nbsp; &lt;/span&gt;Next time, I’ll show how to marshal calls between the VST host and the managed plugin.&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=377445" width="1" height="1"&gt;</content><author><name>jimski3000</name><uri>http://weblogs.asp.net/members/jimski3000.aspx</uri></author></entry><entry><title>Eat Static</title><link rel="alternate" type="text/html" href="http://weblogs.asp.net/jarnold/archive/2004/10/11/240827.aspx" /><id>http://weblogs.asp.net/jarnold/archive/2004/10/11/240827.aspx</id><published>2004-10-11T17:13:00Z</published><updated>2004-10-11T17:13:00Z</updated><content type="html">&lt;p&gt;&lt;font face="Verdana" size="2"&gt;I answered a question in a C# forum the other day from someone looking to do runtime subclassing, similar to &lt;a href="http://nmock.truemesh.org"&gt;NMock&lt;/a&gt;.&amp;nbsp; The question&amp;nbsp;went something like this:&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;"How do I&amp;nbsp;overload the equals/not-equals operators&amp;nbsp;for&amp;nbsp;my dynamically-created class?"&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;Well, being hot shit at code generation, I immediately responded with something like this:&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;"It's easy - just define two static methods called 'op_Equality' and 'op_InEquality'".&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;Then&amp;nbsp;I actually tried it.&amp;nbsp; Although it's quite possible to define and emit these methods correctly, it doesn't do you much good.&amp;nbsp; Operator overloads are resolved at compile-time, not at run-time, so&amp;nbsp;for a statement like this:&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;bool areEqual = (foo1 == foo2);&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;if you have overloaded '==' and '!=' on type Foo in the usual manner (ie statically), the compiler will find the overload and emit MSIL similar to this:&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;ldloc.0&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;ldloc.1&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;call Foo::op_Equality(Foo, Foo)&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;If it doesn't find an overload, it will emit the regular comparison opcode:&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;ldloc.0&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;ldloc.1&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;ceq&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;And that's the problem.&amp;nbsp; Because operators are static, there are no virtual calls to exploit - if your type doesn't define an overload at compile-time, you don't get a chance to "add" one at run-time and let late-binding find it.&amp;nbsp; Same reason we don't mock static or non-virtual calls in NMock.&lt;/font&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=240827" width="1" height="1"&gt;</content><author><name>jimski3000</name><uri>http://weblogs.asp.net/members/jimski3000.aspx</uri></author></entry><entry><title>How big are your privates?</title><link rel="alternate" type="text/html" href="http://weblogs.asp.net/jarnold/archive/2004/09/15/229893.aspx" /><id>http://weblogs.asp.net/jarnold/archive/2004/09/15/229893.aspx</id><published>2004-09-15T13:05:00Z</published><updated>2004-09-15T13:05:00Z</updated><content type="html">&lt;p&gt;&lt;font face="Verdana" size="2"&gt;There has recently been, and will continue to be, increased noise about Team System and its assorted components.&amp;nbsp; The most widely discussed seems to be the unit testing piece, which is encouraging on one level because it means people are interested in unit testing.&amp;nbsp; I have yet to see anyone ask the question "why?", however, as we already have&amp;nbsp;several&amp;nbsp;perfectly good testing frameworks (one of which VSTS blatently clones), with perfectly good (better than VSTS, even) integration with Visual Studio.&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;Lacking, also, is much contrarian opinion on the "Create Tests" feature in VSTS.&amp;nbsp; I'll&amp;nbsp;state my bias up-front - I think it's a Bad Thing - but I think if Microsoft are going to offer the feature, they should also provide guidance on how they think it should be used.&amp;nbsp; I have several objections to this feature.&amp;nbsp; First, it assumes that there is a one-to-one mapping between methods and tests (even down to the semi-configurable test-name generation).&amp;nbsp; Second, it promotes a test-last culture.&amp;nbsp; Third, it makes it super-easy to test private methods.&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;I'll expand on those points in reverse.&amp;nbsp; Whether to test private methods is not one of those issues which has an easy, widely-accepted answer, but there are other significant questions which Microsoft should address before jumping off the fence:&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;* Is unit-testing about testing features or implementation?&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;* Should every piece of code within a class be testable via its public interface?&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;* Does coupling your tests to private implementation hinder or facilitate refactoring?&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;* Does the implementation of the xxxAccessor objects hinder or facilitate refactoring?&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;* Are private methods a code smell?&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;* Do framework developers have different API concerns to closed-audience developers?&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;I think providing an easy way to test private methods solves the wrong problem.&amp;nbsp; Before reaching for the reflection, ask yourself why you can't test that method via the public methods which use it, and consider changing your class-under-test to be more easily testable.&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;The second point I consider the most important.&amp;nbsp; Visual Studio (even 2005) already offers very little to the test driven developer, and I see "Create Tests" as a backward step.&amp;nbsp; How about a "Create Code" feature instead?&amp;nbsp; Whidbey offers "Generate Method Stub".&amp;nbsp; Would it kill them to add Generate Class, Interface, Field, Local?&amp;nbsp; And the expansions don't count - go and look at IntelliJ, or Eclipse, or even ReSharper.&amp;nbsp; By making it so hard to drive development from your unit tests, it's no wonder that some developers view TDD, and programming by intention in general, with suspicion.&amp;nbsp; Much easier to hack away inside a class and add tests later, that way the "flow" isn't interrupted with the constant flipping between unit tests, code-under-test, and&amp;nbsp;the Solution Explorer.&amp;nbsp; I was at a developer lab for VSTS recently and, I'm not kidding, the presenter offered Generate Method Stub as Visual Studio's support for TDD.&amp;nbsp; He even dedicated a slide to it.&amp;nbsp; I work with guys who would have laughed out loud at that.&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;Finally, the idea that each method on a class only needs one test method (one called 'SomeMethodTest' at that) should seem weird to any unit-tester, even those not doing TDD.&amp;nbsp; What are you meant to do with the generated test methods?&amp;nbsp; Put all your assertions about each method in one test?&amp;nbsp; Add more tests as you go, thus instantly screwing up the naming conventions?&amp;nbsp; What about tests which involve more than one method on the same class?&amp;nbsp; What if you want to name your test methods after the behaviour they test, rather than the methods they exercise?&amp;nbsp; &lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;It just hasn't been thought through.&amp;nbsp; If it had, they might have realised that providing lower level features to support code generation from the editor would have provided a far more flexible, and less dangerous, solution.&lt;/font&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=229893" width="1" height="1"&gt;</content><author><name>jimski3000</name><uri>http://weblogs.asp.net/members/jimski3000.aspx</uri></author></entry><entry><title>Visual Studio.Net 2001</title><link rel="alternate" type="text/html" href="http://weblogs.asp.net/jarnold/archive/2004/08/20/217813.aspx" /><id>http://weblogs.asp.net/jarnold/archive/2004/08/20/217813.aspx</id><published>2004-08-20T17:27:00Z</published><updated>2004-08-20T17:27:00Z</updated><content type="html">&lt;p&gt;&lt;font face="Verdana" size="2"&gt;On the last project I was on, there were a lot of ex-Java guys who were used to working with IntelliJ and Eclipse, and who derived endless pleasure&amp;nbsp;from ridiculing Visual Studio's comparative shortcomings.&amp;nbsp; They renamed it 'Visual Retardio' ('Visual Subbuteo' came a close second).&amp;nbsp; When Jetbrains started the ReSharper EAP earlier this year we all jumped on board in a flash, and stuck through months of pain and suffering (it was nicknamed 'ReBlunter' for a time) until finally the finished product was released.&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;Then Visual Studio 2005 went beta, and I was hoping that Microsoft would now have an IDE that was competetive with the best Java IDEs, and that would give us all the things we had become used to with ReSharper. &lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;After using VS.Net 2005 for a few weeks, and having used VS.Net 2003 with ReSharper for the last six months or so, I have come to realise the following:&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;I &lt;em&gt;much &lt;/em&gt;prefer&amp;nbsp;VS.Net 2003 + ReSharper over VS.Net 2005, for the following reasons:&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;&lt;strong&gt;1) Refactoring&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;font face="Verdana" size="2"&gt;ReSharper's refactoring menu is context sensitive, and will only present those which are relevant to the current cursor location.&amp;nbsp; 2005 just gives you all of them, then throws an error when you try and use one in the wrong place.&lt;/font&gt;&lt;/li&gt; &lt;li&gt;&lt;font face="Verdana" size="2"&gt;ReSharper has Introduce variable.&amp;nbsp; I use this &lt;em&gt;all&lt;/em&gt; the time.&lt;/font&gt;&lt;/li&gt; &lt;li&gt;&lt;font face="Verdana" size="2"&gt;ReSharper has a sweet inline rename for local variables and parameters, and will even suggest names for you based on the type.&lt;/font&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;&lt;strong&gt;2) Highlighting&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;font face="Verdana" size="2"&gt;ReSharper will colour &lt;font color="#000000"&gt;fields&lt;/font&gt;, local variables and methods.&lt;/font&gt;&lt;/li&gt; &lt;li&gt;&lt;font face="Verdana" size="2"&gt;It will colour unused or unreachable code.&lt;/font&gt;&lt;/li&gt; &lt;li&gt;&lt;font face="Verdana" size="2"&gt;It will highlight all usages of a field/variable/method within a file.&lt;/font&gt;&lt;/li&gt; &lt;li&gt;&lt;font face="Verdana" size="2"&gt;Type this into any method in 2005:&lt;/font&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Console.WroteLine();&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;What happens?&amp;nbsp; Very little.&amp;nbsp; Try it&amp;nbsp;with ReSharper and it will colour 'WroteLine' in red, and present you with a drop-down list of all the valid Console methods (it will even narrow the list to the best matches).&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;&lt;strong&gt;4) Code Navigation&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;font face="Verdana" size="2"&gt;2005 now has a usable Goto Definition, but you can't navigate to sub or parent types.&lt;/font&gt;&lt;/li&gt; &lt;li&gt;&lt;font face="Verdana" size="2"&gt;2005 now has Find References, but it &lt;em&gt;doesn't find references&lt;/em&gt;.&amp;nbsp; It finds symbols matching the currently selected element, which is a very different thing.&amp;nbsp; If I'm looking for references of a method, I don't want to be presented with a list of similar-looking methods on other classes, and I don't want the &lt;em&gt;method I just selected&lt;/em&gt; to be present in that list.&amp;nbsp; It's just broken.&lt;/font&gt;&lt;/li&gt; &lt;li&gt;&lt;font face="Verdana" size="2"&gt;Guess what?&amp;nbsp; Both these features really work in ReSharper.&amp;nbsp; &lt;em&gt;And&lt;/em&gt; it has decent 'Find Type' and 'Find Method' feature, which narrows the list as you type.&lt;/font&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;&lt;strong&gt;5) Quick Fixes / Smart Tags&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;font face="Verdana" size="2"&gt;I'm no big-city usability expert &amp;lt;gasp!&amp;gt;, but&amp;nbsp;to me, Smart Tags have a big failing: they require three steps to select an option via the keyboard.&amp;nbsp; First, you&amp;nbsp;use a hot-key to get the menu up.&amp;nbsp; Then, you move the cursor down to the option you want, and then you hit return.&amp;nbsp; This wouldn't be so bad, but if there's only one option in the list, you still have to navigate to it!&amp;nbsp; ReSharper's Quick Fixes automatically select the first or only option in the list, and in some cases&amp;nbsp;(if you use a type without importing it with a 'using' statement, for example) it will bring up a tooltip with the relevant option, requiring only one key press to activate it.&lt;/font&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;&lt;strong&gt;6) Intellisense&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;font face="Verdana" size="2"&gt;ReSharper will give you a list of types which haven't yet been imported.&amp;nbsp; When you select one, it will complete your code &lt;em&gt;and&lt;/em&gt; add the 'using' statement for you. &amp;nbsp;It will also auto-complete variable assignments, including brackets and a semi-colon if required.&lt;/font&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;&lt;strong&gt;7) 'Miscellaneous'&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;font face="Verdana" size="2"&gt;ReSharper's 'Extend Selection' feature is quite hard to explain, but very useful.&amp;nbsp; Essentially it's like ctrl-click, but it selects outwards from the current cursor position, and is scope-aware (eg it will select&amp;nbsp;the name of a&amp;nbsp;method call, then the call + its parameters, then anything enclosing that, then anything enclosing that, right up to the file level).&lt;/font&gt;&lt;/li&gt; &lt;li&gt;&lt;font face="Verdana" size="2"&gt;Duplicate Line - like [ctrl-c, cursor down, ctrl-v], but all in one go.&lt;/font&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;I really could go on, but this sounds too much like an advertisement already.&amp;nbsp; Suffice to say that I am disappointed by the first beta of 2005, and I hope that Microsoft can&amp;nbsp;improve things&amp;nbsp;a little by the time they release.&amp;nbsp; I am looking forward to the day when Visual Studio really understands code, rather than being a glorified text editor.&lt;/font&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=217813" width="1" height="1"&gt;</content><author><name>jimski3000</name><uri>http://weblogs.asp.net/members/jimski3000.aspx</uri></author></entry><entry><title>Double whammy</title><link rel="alternate" type="text/html" href="http://weblogs.asp.net/jarnold/archive/2004/08/18/216604.aspx" /><id>http://weblogs.asp.net/jarnold/archive/2004/08/18/216604.aspx</id><published>2004-08-18T17:01:00Z</published><updated>2004-08-18T17:01:00Z</updated><content type="html">&lt;p&gt;&lt;font face="Verdana" size="2"&gt;Anyone who has passed a managed delegate&amp;nbsp;as a parameter&amp;nbsp;to an unmanaged function will be familiar with the oh-dear-the-GC-just-pulled-the-rug-from-under-me NullReferenceException you get if you don't keep save a reference to that delegate.&amp;nbsp; It's hard to debug, but once you are aware of the problem you tend not to make the same mistake again.&amp;nbsp; Or. So. I. Thought.&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;So I'm merrily hacking away with .Net 2.0, trying out the new flashy bits like generics and anonymous methods, when I tried out the new type-inference features in C#.&amp;nbsp; Essentially this means that C# creates the delegate for you, and code like this:&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;button.Click += new ClickEventHandler(OnClick);&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;can be simplified like this:&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;button.Click += OnClick;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;which is much nicer.&amp;nbsp; It also means you can pass delegates to, you guessed it, unmanaged code without explicitly declaring a new delegate instance, and then spend a happy few hours debugging a stack-less NullReferenceException, safe in the knowledge that the problem &lt;em&gt;can't&lt;/em&gt; be related to the delegate being garbage collected, because you'd never make that mistake again.&amp;nbsp; In my defence, I was clearly drunk on syntactic sugar.&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;Which leads me to the second whammy:&amp;nbsp;in 2.0, you don't always get a NullReferenceException in this situation.&amp;nbsp; Sometimes, you get an AccessViolationException, which is a new type meant to help distinguish between null references and other types of AVs.&amp;nbsp; Unfortunately, the CLR seems to be inconsistent&amp;nbsp;about which one&amp;nbsp;it chooses to throw (I was getting NRE&amp;nbsp;on some runs&amp;nbsp;and AVE on others) and led me to investigate the wrong flavour of bug.&lt;/font&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=216604" width="1" height="1"&gt;</content><author><name>jimski3000</name><uri>http://weblogs.asp.net/members/jimski3000.aspx</uri></author></entry><entry><title>Oh yes, we're fully XML enabled</title><link rel="alternate" type="text/html" href="http://weblogs.asp.net/jarnold/archive/2004/08/16/215150.aspx" /><id>http://weblogs.asp.net/jarnold/archive/2004/08/16/215150.aspx</id><published>2004-08-16T16:08:00Z</published><updated>2004-08-16T16:08:00Z</updated><content type="html">&lt;p&gt;&lt;font face="Verdana" size="2"&gt;I missed one off the &lt;A href="http://weblogs.asp.net/jarnold/archive/2004/08/10/211969.aspx"&gt;Programming by Superstition&lt;/a&gt; list:&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;&lt;font color="#000000"&gt;&lt;em&gt;Myth:&lt;/em&gt;&lt;/font&gt; &lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" color="#ff0000" size="2"&gt;DataSets&amp;nbsp;store their data internally as XML.&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;&lt;em&gt;Rationale:&lt;/em&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;Microsoft said so!&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;When ADO.Net was announced, there was a lot of marketing guff about how it was "based around" XML, and how XML was a "first-class citizen".&amp;nbsp; After all, it was the year 2000, and no CxO worth their salt&amp;nbsp;was going to buy into a solution that wasn't "fully XML enabled".&amp;nbsp; In fact, there is at least one article on MSDN which refers to DataSets being "natively an XML structure", so perhaps Microsoft&amp;nbsp;believed their own hype.&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;&lt;em&gt;Truth:&lt;/em&gt;&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;DataSets aren't really the key structure here - they don't do much more than group DataTables together.&amp;nbsp; In turn, DataTables group DataColumns together, and DataRows act as a "horizontal slice" into a group of DataColumns.&amp;nbsp; Going one level further, a DataColumn is just a wrapper around one of a number of classes inheriting from DataStorage (BooleanStorage, ByteStorage, CharStorage and so on), and &lt;em&gt;those&lt;/em&gt;&amp;nbsp;are just strongly-typed arrays with a bit of collection-management logic thrown on top of them.&amp;nbsp; So, no XML.&amp;nbsp; Sorry&lt;/font&gt;.&lt;/p&gt; &lt;p&gt;&lt;font face="Verdana" size="2"&gt;&lt;em&gt;(And just for the record,&amp;nbsp;they would have been feckin stupid to have used XML anyway).&lt;/em&gt;&lt;/font&gt;&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=215150" width="1" height="1"&gt;</content><author><name>jimski3000</name><uri>http://weblogs.asp.net/members/jimski3000.aspx</uri></author></entry><entry><title>Programming by superstition</title><link rel="alternate" type="text/html" href="http://weblogs.asp.net/jarnold/archive/2004/08/10/211969.aspx" /><id>http://weblogs.asp.net/jarnold/archive/2004/08/10/211969.aspx</id><published>2004-08-10T15:52:00Z</published><updated>2004-08-10T15:52:00Z</updated><content type="html">&lt;P&gt;&lt;FONT face=Verdana size=2&gt;Myths and half-truths around .Net and C# best-practices crop up frequently in discussion forums, with rationales ranging from utterly believable to indefensible.&amp;nbsp; I thought I would catalogue the ones I see most frequently.&amp;nbsp; I realise I am leaving myself open to ridicule by prefixing my 'corrections' with 'Truth', but someone has to make a stand :-)&amp;nbsp; Feel free to correct me.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana color=#000000 size=2&gt;&lt;EM&gt;Myth:&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana color=#ff0000 size=2&gt;Always set your objects to null (or Nothing) when you're done with them.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;&lt;EM&gt;Rationale:&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;The garbage collector won't be able to free an object if any another object is referencing it.&amp;nbsp; &lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;&lt;EM&gt;Truth:&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;The garbage collector&amp;nbsp;can free any object that is unreachable from any of the various heap roots.&amp;nbsp; If object A references object B, and object A is unreachable, object B can be released if it has no other references from reachable objects.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;&lt;FONT face=Verdana size=2&gt;Myth:&lt;/FONT&gt;&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana color=#ff0000 size=2&gt;StringBuilder.Append() is always faster than String.Concat().&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;&lt;EM&gt;Rationale:&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;String operations always create new string objects, whereas StringBuilder uses internal magic to avoid expensive allocations.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;&lt;EM&gt;Truth:&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;Using a StringBuilder is sometimes (even usually) faster than using simple string concatenation, but there is a performance cutover point.&amp;nbsp; Various people have found this to be around the 5 - 10 concatenations mark.&amp;nbsp; The bottom line being that replacing this:&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;&lt;FONT color=#000080&gt;&lt;FONT color=#0000ff&gt;string&lt;/FONT&gt; &lt;/FONT&gt;fullName = firstName + " " + lastName;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;with this:&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;StringBuilder builder = new StringBuilder();&lt;/FONT&gt; &lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;builder.Append(firstName);&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;builder.Append(" ");&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;builder.Append(lastName);&lt;/FONT&gt; &lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;&lt;FONT color=#0000ff&gt;string&lt;/FONT&gt; fullName = builder.ToString();&lt;/FONT&gt;&lt;/P&gt;&lt;FONT face=Verdana size=2&gt;is rather pointless.&amp;nbsp; Remember that StringBuilder is itself reference type, and&amp;nbsp;has the associated allocation/deallocation costs.&lt;/FONT&gt; 
&lt;P&gt;&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;&lt;FONT face=Verdana size=2&gt;Myth:&lt;/FONT&gt;&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana color=#ff0000 size=2&gt;Strings are&amp;nbsp;passed to, and returned from, methods&amp;nbsp;by-value.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;&lt;EM&gt;Rationale:&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;Strings are immutable, therefore they must have value type semantics, therefore must be copied by value.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;&lt;EM&gt;Truth:&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;Strings have certain value-type semantics (namely, immutability), but are otherwise heap-based reference types, and &lt;EM&gt;references&lt;/EM&gt; to them can indeed be passed or copied.&amp;nbsp; Passing a string reference to or from a method does not copy the underlying string.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;&lt;FONT face=Verdana size=2&gt;Myth:&lt;/FONT&gt;&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana color=#ff0000 size=2&gt;Dispose() releases an object's memory.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;&lt;EM&gt;Rationale:&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;Well, it's called 'Dispose' isn't it?&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;&lt;EM&gt;Truth:&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;Dispose() is merely a convenient place for a class' developer to place any resource clean-up code, in the hope that clients will call it.&amp;nbsp; Dispose does not free the managed memory allocated for an object.&amp;nbsp; The only thing that can do that is the garbage collector; there is no way to deterministically (or manually) release memory.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;&lt;FONT face=Verdana size=2&gt;Myth:&lt;/FONT&gt;&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana color=#ff0000 size=2&gt;Keeping ADO.Net&amp;nbsp;connection objects open for the duration of a [Page/Control/1000-line method]'s lifetime is more efficient than repeatedly opening and closing it around each operation.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;&lt;EM&gt;Rationale:&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;Connecting to a database is expensive, so try to avoid doing it too much.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;&lt;EM&gt;Truth:&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;If your managed provider allows connection pooling, and if pooling is enabled (it is, by default, for the managed SQL Server provider at least), calling Close() on the connection object does not&amp;nbsp;simply close the underlying connection.&amp;nbsp; It merely releases that connection back to the pool where it can be associated with another managed connection object at some time in the future.&amp;nbsp; This is a Good Thing, especially in multi-threaded&amp;nbsp;environments such as ASP.Net,&amp;nbsp;because physical connections are rare resources and should not be coveted.&amp;nbsp; (By the same token, calling Open() on a managed connection when a pooled physical connection is available will simply associate the former with the latter).&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;By keeping a managed connection in an open state, you are preventing objects in other threads from getting hold of them, leading to potential bottlenecks.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;&lt;FONT face=Verdana size=2&gt;Myth:&lt;/FONT&gt;&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana color=#ff0000 size=2&gt;Int32 and int are not the same thing (ditto float/Short, long/Int64, string/String, etc.).&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;&lt;EM&gt;Rationale:&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;They're spelled differently.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;&lt;EM&gt;Truth:&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;The C# simple type keywords are interchangeable with the equivalent BCL types; they are aliases.&lt;/FONT&gt;&lt;/P&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=211969" width="1" height="1"&gt;</content><author><name>jimski3000</name><uri>http://weblogs.asp.net/members/jimski3000.aspx</uri></author></entry><entry><title>The remarkable staying-power of of System.Assembly</title><link rel="alternate" type="text/html" href="http://weblogs.asp.net/jarnold/archive/2004/07/07/175407.aspx" /><id>http://weblogs.asp.net/jarnold/archive/2004/07/07/175407.aspx</id><published>2004-07-07T16:07:00Z</published><updated>2004-07-07T16:07:00Z</updated><content type="html">&lt;P&gt;&lt;FONT face=Verdana size=2&gt;There was an interesting bug in &lt;A href="http://nmock.truemesh.com"&gt;NMock&lt;/A&gt; until recently (it's been fixed) which saw memory usage during unit test runs increase in relation to the total number of mock objects created during the run.&amp;nbsp; The culprit was .Net's inability to unload assemblies from a running AppDomain: each mock was being hosted in a brand new dynamic assembly -&amp;nbsp;which never gets garbage collected -&amp;nbsp;so you could easily end up with a few hundred assemblies languishing in the root domain, eating memory.&amp;nbsp; We fixed it by creating one assembly for all mock types, and by caching identical types for the duration of the test run.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;Today, another bug was raised against NMock, which involved calling GetManifestResourceNames() on all loaded assemblies.&amp;nbsp; Dynamic assembliles will throw NotSupportedException for this&amp;nbsp;method (and for a&amp;nbsp;couple of&amp;nbsp;others), so if any code which relies on this call is used alongside NMock (in this case it was NVelocity), we break it.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;I looked at a couple of workarounds, including saving our mock assembly to disk and reloading it as a static assembly so it could play nicely, but noticed that AssemblyBuilder, which we use to generate the mock assembly, is never garbage collected either!&amp;nbsp; So, kids, be aware that if you generate assemblies at runtime, even just to save them to disk, your AssemblyBuilders will hang around for ever, holding you memory hostage and calling you "poo-breath" behind your back.&amp;nbsp; The solution, of course, is to do your code-gen in a separate AppDomain, which is pretty weak but at least you can throw it away when you're done.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face=Verdana size=2&gt;Does anyone have a good&amp;nbsp;reason why assemblies cannot be garbage collected?&lt;/FONT&gt;&lt;/P&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=175407" width="1" height="1"&gt;</content><author><name>jimski3000</name><uri>http://weblogs.asp.net/members/jimski3000.aspx</uri></author></entry></feed>