コード例 #1
0
        /// <summary>
        /// 绘制波形
        /// </summary>
        /// <param name="win"></param>
        /// <param name="waveQueueData"></param>
        /// <param name="polylineStyle"></param>
        /// <param name="id">区分线程,调试用</param>
        static void DrawWave(Window win, FixedQueueArray <float> waveQueueData, bool polylineStyle, int id = 0)
        {
            var lineColor = Color.FromRgb(17, 125, 187);
            var lineBrush = new SolidColorBrush(lineColor);

            lineBrush.Freeze();
            var pen = new Pen(lineBrush, 1);

            pen.Freeze();

            // WindowGraphics 这是一个可以跨进程绘制窗体的类
            // 使用 WPF 中的 DrawingContext 进行绘制
            var wg = new WindowGraphics(win);

            while (true)
            {
                Task.Delay(20).Wait();

                using (var drawCtx = wg.OpenDraw()) {
                    Span <float> waveData = new float[(int)drawCtx.Size.Width];
                    waveQueueData.Read(waveData);

                    drawCtx.DrawingContext.DrawRectangle(Brushes.White, null, new Rect(drawCtx.Size));
                    DrawBorder(drawCtx);

                    var     h2 = drawCtx.Size.Height / 2;
                    Point[] points;
                    if (polylineStyle)
                    {
                        const int step = 10;
                        points = new Point[waveData.Length / step + 1];
                        for (int i = 0; i < points.Length; i++)
                        {
                            double sum   = 0;
                            int    count = 0;
                            for (int j = 0; j < step; j++)
                            {
                                if (i * step + j >= waveData.Length)
                                {
                                    break;
                                }
                                sum += waveData[i * step + j];
                                count++;
                            }
                            var avg = count > 0 ? sum / count : 0;
                            points[i] = new Point(i * step, h2 + avg * h2);
                        }
                        points[points.Length - 1].X = drawCtx.Size.Width;
                    }
                    else
                    {
                        points = new Point[waveData.Length];
                        for (int i = 0; i < waveData.Length; i++)
                        {
                            double y = waveData[i] * h2;
                            points[i] = new Point(i, h2 + y);
                        }
                    }

                    var geometry = new StreamGeometry();
                    using (var geometryCtx = geometry.Open()) {
                        geometryCtx.BeginFigure(points[0], false, false);
                        geometryCtx.PolyLineTo(points, true, true);
                    }
                    geometry.Freeze();
                    drawCtx.DrawingContext.DrawGeometry(null, pen, geometry);
                }
            }
        }
コード例 #2
0
        static void Main(string[] args)
        {
            // 先注入一个dll,Hook要绘制的窗体的WndProc函数,吃掉WM_PAINT消息,不然任务管理器会闪烁
            var dllInjectionContext = DllInjection.Injection("Taskmgr", Path.Combine(Environment.CurrentDirectory, "HookWinProc.dll"));

            controlCtrlDelegate = type => {
                switch (type)
                {
                case 0:                         // CTRL_C_EVENT
                case 2:                         // CTRL_CLOSE_EVENT
                case 5:                         // CTRL_LOGOFF_EVENT
                case 6:                         // CTRL_SHUTDOWN_EVENT
                                                // 控制台关闭后清理掉注入的dll
                    DllInjection.FreeLibrary(dllInjectionContext);
                    break;
                }
                return(true);
            };
            SetConsoleCtrlHandler(controlCtrlDelegate, true);


            // 获得CPU核心数,其实我之后的代码已经写死默认是4个核心的情况
            var kernelCount = Environment.ProcessorCount;

            var hwnd = WinApi.FindWindow("TaskManagerWindow", "任务管理器");
            var root = new Window(hwnd);

            if (root.Childs.Count == 1 && root.Childs[0].ClassName == "NativeHWNDHost")
            {
                root = root.Childs[0];
            }
            else
            {
                throw new Exception("未找到窗体");
            }
            if (root.Childs.Count == 1 && root.Childs[0].ClassName == "DirectUIHWND")
            {
                root = root.Childs[0];
            }
            else
            {
                throw new Exception("未找到窗体");
            }

            // 拿到4个子窗体,但是顺序还不确定
            var drawWindows = root.Childs
                              .Where(w => w.ClassName == "CtrlNotifySink" && w.Childs.Count == 1 && w.Childs[0].ClassName == "CvChartWindow")
                              .Select(w => w.Childs[0])
                              .OrderByDescending(w => w.Rect.Width * w.Rect.Height)
                              .Take(kernelCount)
                              .ToArray();

            // 以下是捕获声音有关的代码,使用的是WASAPI Loopback的方式
            var screenWidth = WinApi.GetScreenWidth();
            FixedQueueArray <float> leftWaveQueue = new FixedQueueArray <float>(screenWidth), rightWaveQueue = new FixedQueueArray <float>(screenWidth);
            IWaveIn loopbackCapture = new WasapiLoopbackCapture();
            var     observer        = new StreamObserver <float>(FFTSize, FFTSize / 2, 2);

            float[] leftFixedWaveData = new float[FFTSize], rightFixedWaveData = new float[FFTSize];

            // 捕获声音
            // leftFixedWaveData 和 rightFixedWaveData 之后会交给绘制频谱的线程处理
            observer.Completed += newData => {
                lock (fftLock) {
                    for (int i = 0; i < leftFixedWaveData.Length; i++)
                    {
                        leftFixedWaveData[i]  = newData[2 * i + 0];
                        rightFixedWaveData[i] = newData[2 * i + 1];
                    }
                }
            };

            // 捕获声音
            // leftWaveQueue 和 rightWaveQueue 之后会交给绘制声波的线程处理
            loopbackCapture.DataAvailable += (_, e) => {
                var waveData   = MemoryMarshal.Cast <byte, float>(new ReadOnlySpan <byte>(e.Buffer, 0, e.BytesRecorded));
                int copyLength = Math.Min(waveData.Length / 2, screenWidth);
                if (copyLength == 0)
                {
                    return;
                }
                Span <float> leftNextData = stackalloc float[copyLength], rightNextData = stackalloc float[copyLength];
                for (int i = 0; i < copyLength; i++)
                {
                    leftNextData[i]  = waveData[2 * i + 0];
                    rightNextData[i] = waveData[2 * i + 1];
                }
                leftWaveQueue.Write(leftNextData);
                rightWaveQueue.Write(rightNextData);
                observer.Write(waveData);
            };
            loopbackCapture.StartRecording();

            var  sampleRate    = loopbackCapture.WaveFormat.SampleRate;
            bool polylineStyle = false;

            // 找出对应位置的子窗体
            var leftTopWin     = drawWindows.OrderBy(w => w.Rect.X + w.Rect.Y).First();
            var rightTopWin    = drawWindows.OrderBy(w => - w.Rect.X + w.Rect.Y).First();
            var leftBottomWin  = drawWindows.OrderBy(w => w.Rect.X - w.Rect.Y).First();
            var rightBottomWin = drawWindows.OrderBy(w => - w.Rect.X - w.Rect.Y).First();

            void StartThread(Action action)
            {
                new Thread(new ThreadStart(action))
                {
                    Priority = ThreadPriority.Highest
                }.Start();
            }

            // 启动线程开始捕获声音并绘制,每个窗体一个线程
            StartThread(() => DrawWave(leftTopWin, leftWaveQueue, polylineStyle, 1));
            StartThread(() => DrawWave(rightTopWin, rightWaveQueue, polylineStyle, 2));
            StartThread(() => DrawSpectrum(leftBottomWin, leftFixedWaveData, sampleRate, polylineStyle, 3));
            StartThread(() => DrawSpectrum(rightBottomWin, rightFixedWaveData, sampleRate, polylineStyle, 4));

            Thread.Sleep(Timeout.Infinite);
        }