private void StartDrones()
        {
            m_bagpipeOutputs = new SoundOutput[4];

            m_bagpipeOutputs[0]                  = new SoundOutput();
            m_bagpipeOutputs[0].Sample           = m_samples[0];
            m_bagpipeOutputs[0].DesiredFrequency = m_samples[0].Frequency;
            m_bagpipeOutputs[0].RandomizePosition();
            m_bagpipeOutputs[0].Volume = 0.7f;
            m_bagpipeOutputs[0].Start();

            m_bagpipeOutputs[1]                  = new SoundOutput();
            m_bagpipeOutputs[1].Sample           = m_samples[1];
            m_bagpipeOutputs[1].DesiredFrequency = m_samples[1].Frequency;
            m_bagpipeOutputs[1].RandomizePosition();
            m_bagpipeOutputs[1].Volume = 0.7f;
            m_bagpipeOutputs[1].Start();

            m_bagpipeOutputs[2]                  = new SoundOutput();
            m_bagpipeOutputs[2].Sample           = m_samples[1];
            m_bagpipeOutputs[2].DesiredFrequency = m_samples[1].Frequency + 1;
            m_bagpipeOutputs[2].RandomizePosition();
            m_bagpipeOutputs[2].Volume = 0.7f;
            m_bagpipeOutputs[2].Start();

            m_bagpipeOutputs[3]                  = new SoundOutput();
            m_bagpipeOutputs[3].Sample           = m_chanterNotes[5];
            m_bagpipeOutputs[3].DesiredFrequency = m_chanterNotes[5].Frequency;
            m_bagpipeOutputs[3].RandomizePosition();
            m_bagpipeOutputs[3].Start();
        }
        private void MainForm_Load(object sender, EventArgs e)
        {
            m_samples = new Sample[]
            {
                new Sample("Base Drone 119.raw", 119),
                new Sample("Tenor drone 239.raw", 239),
                new Sample("Chanter LowG 420.raw", 420),
                new Sample("Chanter LowA 477.raw", 477),
                new Sample("Chanter B 536.raw", 536),
                new Sample("Chanter C 594.raw", 594),
                new Sample("Chanter D 627.raw", 627),
                new Sample("Chanter E 716.raw", 716),
                new Sample("Chanter F 805.raw", 805),
                new Sample("Chanter HighG 815.raw", 815),
                new Sample("Chanter HighA 943.raw", 943),
            };

            m_chanterNotes = new Sample[]
            {
                new Sample("Chanter LowG 420.raw", 420),
                new Sample("Chanter LowA 477.raw", 477),
                new Sample("Chanter B 536.raw", 536),
                new Sample("Chanter C 594.raw", 594),
                new Sample("Chanter D 627.raw", 627),
                new Sample("Chanter E 716.raw", 716),
                new Sample("Chanter F 805.raw", 805),
                new Sample("Chanter HighG 815.raw", 815),
                new Sample("Chanter HighA 943.raw", 943),
            };

            var offsetFrequencyScale = (float)Math.Pow(2.0f, 1.0f / 12.0f);
            var numOffsetsPerSide    = 3;

            var y = 0;

            foreach (var s in m_samples)
            {
                var l = new Label();
                l.Text  = s.Filename;
                l.Top   = y;
                l.Width = 130;
                pnlSamples.Controls.Add(l);

                var x = l.Width;

                for (int i = -numOffsetsPerSide; i <= numOffsetsPerSide; ++i)
                {
                    var b = new Button();
                    b.Left  = x;
                    b.Top   = y;
                    b.Width = 50;
                    b.Text  = i.ToString();
                    b.Tag   = new SamplePlayback {
                        Sample = s, Frequency = s.Frequency * (float)Math.Pow(offsetFrequencyScale, i)
                    };
                    b.Click += SampleButtonClicked;
                    pnlSamples.Controls.Add(b);
                    x += b.Width;
                }
                y += 25;
            }

            m_soundOutput = new SoundOutput();
            m_soundOutput.Start();

            //StartDrones();

            //m_serialTask = Task.Run(() => DumpSerial());
            m_serialTask = Task.Run(() => ReadTunerFrequencyFromSerial());
        }
        TestResult TestPoint(SoundOutput so, Sample sample, float frequency, int numReadings)
        {
            // Flush out the sound pipeline before starting the new test
            so.Sample = null;

            WaitForRejectedReadings(1);

            so.Sample           = sample;
            so.DesiredFrequency = frequency;

            var result = new TestResult();

            result.Frequency  = frequency;
            result.SampleName = sample.Filename;

            var seenFirstValidReading = false;

            for (; result.NumValidReadings < numReadings;)
            {
                var tr = DequeueTunerReading();

                // Wait until our first non-rejected reading
                if (!seenFirstValidReading && (tr.InstantFrequency < 0.0f))
                {
                    //Console.WriteLine("(waiting until first valid reading to start sampling)");
                    continue;
                }

                seenFirstValidReading = true;

                result.Readings.Add(tr);

                if (tr.InstantFrequency >= 0.0f)
                {
                    ++result.NumValidReadings;
                    var valid       = IsReadingValid(frequency, tr);
                    var octaveError = IsReadingOctaveError(frequency, tr);

                    if (valid)
                    {
                        ++result.NumPassed;
                    }
                    else
                    {
                        ++result.NumFailed;
                        if (octaveError)
                        {
                            ++result.NumWrongOctaveReadings;
                        }
                    }

                    Console.WriteLine("Expected: {0,7:F2} got: {1,7:F2}  {2,7:F2}  ({3} - {4})", frequency, tr.InstantFrequency, valid ? " " : (octaveError ? "O" : "X"), tr.MinSignalFrequency, tr.MaxSignalFrequency);
                }
                else
                {
                    ++result.NumRejectedReadings;
                    Console.WriteLine("(Rejected)");
                }
            }

            //Console.WriteLine("{0} readings, {1} in tolerance", numReadings, inTolerance);
            return(result);
        }