public override void RenderFrame(UnsafeColor *ptr)
 {
     if (redrawNeeded)
     {
         RedrawFrame(ptr);
     }
 }
예제 #2
0
        private unsafe void FindHorizMarkers(UnsafeColor *ptr)
        {
            long   division   = horizFreqDivision;
            double hzPerPixel = (double)bandwidth / SpectrumWidth;
            long   freq       = ((centerFreq / 2) / division) * division; //Find the leftmost frequency, aligned to the division set

            while (true)
            {
                //Calculate
                int px = (int)(freq / hzPerPixel);
                if (px > SpectrumWidth)
                {
                    break;
                }

                //Render
                if (px >= 0)
                {
                    AddHorizontalMarker(ptr, ctx, px, freq);
                }

                //Update
                freq += division;
            }
        }
예제 #3
0
        public override unsafe void InitFrame(UnsafeColor *ptr)
        {
            //Call main
            base.InitFrame(ptr);

            long division   = horizFreqDivision;
            long hzPerPixel = (long)bandwidth / SpectrumWidth;
            long freqOffset = centerFreq - (bandwidth / 2);
            long freq       = 0;

            if (freqOffset % division != 0)
            {
                freq += division - (freqOffset % division);
            }
            while (true)
            {
                //Calculate
                long px = freq / hzPerPixel;
                if (px > SpectrumWidth)
                {
                    break;
                }

                //Render
                AddHorizontalMarker(ptr, ctx, (int)px, freq + freqOffset);

                //Update
                freq += division;
            }

            //Automatically process the points
            AutoAddVerticalMarkers(ptr, ctx);
        }
        public override unsafe void InitFrame(UnsafeColor *ptr)
        {
            //Call main
            base.InitFrame(ptr);

            long hzPerPixel = (long)demodulator.MpxSampleRate / 2 / SpectrumWidth;
            long freq       = 0;

            while (true)
            {
                //Calculate
                long px = freq / hzPerPixel;
                if (px > SpectrumWidth)
                {
                    break;
                }

                //Render
                AddHorizontalMarker(ptr, ctx, (int)px, freq);

                //Update
                freq += 19000;
            }


            //Automatically process the points
            AutoAddVerticalMarkers(ptr, ctx);
        }
예제 #5
0
 public unsafe void RenderLine(UnsafeColor *ptr, int canvasWidth, char[] chars, int count, UnsafeColor color)
 {
     for (int i = 0; i < Math.Min(chars.Length, count); i++)
     {
         RenderCharacter(ptr, canvasWidth, chars[i], color);
         ptr += width + 1;
     }
 }
예제 #6
0
 protected override void DrawableViewReset(int width, int height)
 {
     //Fill with black
     for (int y = 0; y < height; y++)
     {
         UnsafeColor *lnDst = pixels + (y * width);
         for (int x = 0; x < width; x++)
         {
             lnDst[x] = new UnsafeColor(0, 0, 0);
         }
     }
 }
예제 #7
0
        public unsafe void RenderCharacter(UnsafeColor *ptr, int canvasWidth, char character, UnsafeColor color)
        {
            //Try to find character data
            if (character == (char)0x00 || character == '\n' || character == '\r')
            {
                //Ignore. This is a null or invisible character
            }
            else if (characters.ContainsKey(character))
            {
                //Copy pixel data
                var cha = characters[character];
                for (int i = 0; i < width * fullHeight; i++)
                {
                    //Check if this is black to save CPU
                    if (cha.payload[i] == 0)
                    {
                        continue;
                    }

                    //Get location in memory
                    UnsafeColor *pxl = ptr + (canvasWidth * (i / width)) + (i % width);

                    //Update
                    if (cha.payload[i] == byte.MaxValue)
                    {
                        //Full brightness. Cheat
                        *pxl = color;
                    }
                    else
                    {
                        //Mix
                        float scale  = (float)cha.payload[i] / 256;
                        float scaleR = 1 - scale;
                        *     pxl    = new UnsafeColor(
                            (byte)(((*pxl).r * scaleR) + (color.r * scale)),
                            (byte)(((*pxl).g * scaleR) + (color.g * scale)),
                            (byte)(((*pxl).b * scaleR) + (color.b * scale))
                            );
                    }
                }
            }
            else
            {
                //Write a blank spot, as this character isn't found
            }
        }
예제 #8
0
        public override unsafe void InitFrame(UnsafeColor *ptr)
        {
            //Render background for RDS
            FillArea(ptr, 0, Height - RDS_HEIGHT - PADDING - RDS_MARGIN_TOP, Width, PADDING + RDS_MARGIN_TOP + RDS_HEIGHT, new UnsafeColor(RDS_RT_BACKGROUND_BRIGHTNESS, RDS_RT_BACKGROUND_BRIGHTNESS, RDS_RT_BACKGROUND_BRIGHTNESS));
            FillArea(ptr, 0, Height - RDS_HEIGHT - PADDING - RDS_MARGIN_TOP, PS_OFFSET - PADDING, PADDING + RDS_MARGIN_TOP + RDS_HEIGHT, new UnsafeColor(RDS_PS_BACKGROUND_BRIGHTNESS, RDS_PS_BACKGROUND_BRIGHTNESS, RDS_PS_BACKGROUND_BRIGHTNESS));

            //Render border
            FillArea(ptr, 0, 0, Width, BORDER_WIDTH, UnsafeColor.WHITE);
            FillArea(ptr, 0, Height - BORDER_WIDTH, Width, BORDER_WIDTH, UnsafeColor.WHITE);
            FillArea(ptr, 0, 0, BORDER_WIDTH, Height, UnsafeColor.WHITE);
            FillArea(ptr, Width - BORDER_WIDTH, 0, BORDER_WIDTH, Height, UnsafeColor.WHITE);

            //Render title
            ctx.TextRenderer.RenderRawBox(
                ctx.TextRenderer.GetOffsetPixel(ptr, PADDING, PADDING),
                new char[][] { label.ToCharArray() },
                FontStore.SYSTEM_BOLD_20,
                FontAlignHorizontal.Left,
                FontAlignVertical.Center,
                Width - PADDING - PADDING,
                TITLE_HEIGHT,
                new FontColor(1),
                new FontColor(0)
                );

            //Get left offset
            int offsetLeft = PADDING + FontStore.SYSTEM_BOLD_20.MeasureWidth(label.Length) + TITLE_MARGIN_RIGHT;

            //Render sub texts
            ctx.TextRenderer.RenderRawBox(
                ctx.TextRenderer.GetOffsetPixel(ptr, offsetLeft, PADDING),
                new char[][] { subTextA.ToCharArray(), subTextB.ToCharArray() },
                FontStore.SYSTEM_REGULAR_15,
                FontAlignHorizontal.Left,
                FontAlignVertical.Center,
                Width - offsetLeft - PADDING,
                TITLE_HEIGHT,
                new FontColor(0.8f),
                new FontColor(0)
                );
        }
예제 #9
0
        public override unsafe void RenderFrame(UnsafeColor *ptr)
        {
            //If not invalidated, ignore
            if (!invalidated)
            {
                return;
            }

            //Move the pointer down to the beginning of the RDS bar
            ptr += (Width * (RDS_LABEL_OFFSET + RDS_LABEL_HEIGHT));

            //Render PS name
            ctx.TextRenderer.RenderRawBox(
                ctx.TextRenderer.GetOffsetPixel(ptr, PADDING, 0),
                new char[][] { RdsDecoder.psBuffer },
                FontStore.SYSTEM_REGULAR_15,
                FontAlignHorizontal.Left,
                FontAlignVertical.Center,
                FontStore.SYSTEM_REGULAR_15.MeasureWidth(8),
                RDS_HEIGHT,
                new FontColor(1),
                RDS_PS_TEXT_BACKGROUND
                );

            //Render RT
            ctx.TextRenderer.RenderRawBox(
                ctx.TextRenderer.GetOffsetPixel(ptr, PS_OFFSET, 0),
                new char[][] { RdsDecoder.rtBuffer },
                FontStore.SYSTEM_NARROW_15,
                FontAlignHorizontal.Left,
                FontAlignVertical.Center,
                Width - PS_OFFSET - PADDING,
                RDS_HEIGHT,
                new FontColor(0.85f),
                RDS_RT_TEXT_BACKGROUND
                );

            //Set invalidated flag
            invalidated = false;
        }
        private void RedrawFrame(UnsafeColor *ptr)
        {
            //Render indicators
            int offsetRight = PADDING_STATUS_HORIZ;

            offsetRight += DrawStatusIndicator(ptr, offsetRight, demodulator.StereoDetected, 'S', 'T', 'E', 'R', 'E', 'O');
            offsetRight += DrawStatusIndicator(ptr, offsetRight, rds.IsRdsSynced, 'R', 'D', 'S');

            //Render PS name
            int offsetLeft = FontStore.SYSTEM_REGULAR_15.MeasureWidth(8) + PADDING_PS + PADDING_PS;

            ctx.TextRenderer.RenderRawBox(
                ctx.TextRenderer.GetOffsetPixel(ptr, 0, 0),
                new char[][] { rds.psBuffer },
                FontStore.SYSTEM_REGULAR_15,
                FontAlignHorizontal.Center,
                FontAlignVertical.Center,
                offsetLeft,
                HEIGHT,
                new FontColor(1),
                new FontColor(0.12f)
                );

            //Render RT text
            ctx.TextRenderer.RenderRawBox(
                ctx.TextRenderer.GetOffsetPixel(ptr, offsetLeft + PADDING_RT, 0),
                new char[][] { rds.rtBuffer },
                FontStore.SYSTEM_NARROW_15,
                FontAlignHorizontal.Left,
                FontAlignVertical.Center,
                Width - offsetLeft - offsetRight - PADDING_RT - PADDING_RT + PADDING_STATUS_HORIZ,
                HEIGHT,
                new FontColor(1),
                new FontColor(0)
                );

            //Update state
            redrawNeeded = false;
        }
        private int DrawStatusIndicator(UnsafeColor *ptr, int offset, bool active, params char[] name)
        {
            //Layout
            int statusWidth = FontStore.SYSTEM_NARROW_BOLD_15.MeasureWidth(name.Length) + STATUS_PADDING + STATUS_PADDING;

            //Render
            FontColor color = active ? new FontColor(1) : new FontColor(0.4f);

            ctx.TextRenderer.RenderRawBox(
                ctx.TextRenderer.GetOffsetPixel(ptr, Width - offset - statusWidth, STATUS_MARGIN_VERT),
                new char[][] { name },
                FontStore.SYSTEM_NARROW_BOLD_15,
                FontAlignHorizontal.Center,
                FontAlignVertical.Center,
                statusWidth,
                HEIGHT - STATUS_MARGIN_VERT - STATUS_MARGIN_VERT,
                new FontColor(0),
                color
                );

            return(statusWidth + STATUS_MARGIN_HORIZ);
        }
        private unsafe void RunWorker()
        {
            //Open pixel buffer
            byte[]       pixelBuffer       = new byte[generator.Height * generator.Width * sizeof(UnsafeColor)];
            GCHandle     pixelBufferHandle = GCHandle.Alloc(pixelBuffer, GCHandleType.Pinned);
            UnsafeColor *pixelBufferPtr    = (UnsafeColor *)pixelBufferHandle.AddrOfPinnedObject();

            //Create IQ buffer
            UnsafeBuffer iqBuffer    = UnsafeBuffer.Create(generator.BufferSize, sizeof(Complex));
            Complex *    iqBufferPtr = (Complex *)iqBuffer;

            //Create audio buffers
            byte[]   audioBuffer       = new byte[generator.BufferSize * 2 * sizeof(float)];
            GCHandle audioBufferHandle = GCHandle.Alloc(audioBuffer, GCHandleType.Pinned);
            float *  audioBufferPtr    = (float *)audioBufferHandle.AddrOfPinnedObject();
            float *  audioLBufferPtr   = audioBufferPtr;
            float *  audioRBufferPtr   = (audioLBufferPtr + generator.BufferSize);

            //Create resampler
            ArbitraryFloatResampler resamplerA = new ArbitraryFloatResampler(generator.OutputAudioRate, 48000, generator.BufferSize);
            ArbitraryFloatResampler resamplerB = new ArbitraryFloatResampler(generator.OutputAudioRate, 48000, generator.BufferSize);

            //Create pipes for FFMPEG
            string videoPipeName            = $"SpectrumVideoRenderer{Process.GetCurrentProcess().Id}Video";
            string audioPipeName            = $"SpectrumVideoRenderer{Process.GetCurrentProcess().Id}Audio";
            NamedPipeServerStream videoPipe = new NamedPipeServerStream(videoPipeName, PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, 10000, 10000);
            NamedPipeServerStream audioPipe = new NamedPipeServerStream(audioPipeName, PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, 10000, 10000);

            //Start FFMPEG
            Process encoder = Process.Start(new ProcessStartInfo
            {
                FileName              = "ffmpeg",
                Arguments             = $"-y -f rawvideo -pix_fmt bgra -s {generator.Width}x{generator.Height} -r 30 -i \\\\.\\pipe\\{videoPipeName} -f f32le -ar 48000 -ac 2 -i \\\\.\\pipe\\{audioPipeName} E:\\test_video.mp4",
                RedirectStandardInput = true,
                UseShellExecute       = false
            });

            //Make sure WAV is at the beginning
            wav.PositionSamples = 0;

            //Process
            DateTime startTime          = DateTime.UtcNow;
            DateTime lastProgressUpdate = DateTime.MinValue;
            int      read;

            do
            {
                //Read
                read = wav.Read(iqBufferPtr, generator.BufferSize);

                //Process
                int audioRead = generator.ProcessFrame(iqBufferPtr, pixelBufferPtr, audioLBufferPtr, audioRBufferPtr, read);

                //Write pixels
                if (!videoPipe.IsConnected)
                {
                    videoPipe.WaitForConnection();
                }
                videoPipe.WriteAsync(pixelBuffer, 0, pixelBuffer.Length);

                //Add samples to resamplers
                resamplerA.Input(audioLBufferPtr, audioRead, 1);
                resamplerB.Input(audioRBufferPtr, audioRead, 1);

                //Read audio back and merge it into one stream
                do
                {
                    //Read
                    resamplerA.Output(audioBufferPtr, generator.BufferSize, 2);
                    audioRead = resamplerB.Output(audioBufferPtr + 1, generator.BufferSize, 2);

                    //Write to stream
                    if (!audioPipe.IsConnected)
                    {
                        audioPipe.WaitForConnection();
                    }
                    audioPipe.WriteAsync(audioBuffer, 0, audioRead * 2 * sizeof(float));
                } while (audioRead != 0);

                //Update status if needed
                if ((DateTime.UtcNow - lastProgressUpdate).TotalMilliseconds > 100)
                {
                    float  progress         = (float)wav.PositionSamples / wav.LengthSamples;
                    long   totalSeconds     = (long)((DateTime.UtcNow - startTime).TotalSeconds / progress);
                    long   remainingSeconds = (long)(totalSeconds - (DateTime.UtcNow - startTime).TotalSeconds);
                    string statusText       = $"Rendering... {(remainingSeconds / 60 / 60).ToString().PadLeft(2, '0')}:{((remainingSeconds / 60) % 60).ToString().PadLeft(2, '0')}:{(remainingSeconds % 60).ToString().PadLeft(2, '0')} remaining, {Math.Round(progress*100, 2)}%";
                    Invoke((MethodInvoker) delegate
                    {
                        status.Text       = statusText;
                        progressBar.Value = (int)(progress * 1000);
                    });
                    lastProgressUpdate = DateTime.UtcNow;
                }
            } while (read != 0 && !requestClose);

            //Update status
            Invoke((MethodInvoker) delegate
            {
                status.Text       = "Finishing up rendering...";
                btnCancel.Enabled = false;
            });

            //Tell FFMPEG to stop and wait for it to do so
            audioPipe.Close();
            videoPipe.Close();
            encoder.WaitForExit();

            //Clean up
            audioBufferHandle.Free();
            pixelBufferHandle.Free();
            iqBuffer.Dispose();
            resamplerA.Dispose();
            resamplerB.Dispose();

            //Close
            hasCompleted = true;
            Invoke((MethodInvoker) delegate
            {
                Close();
            });
        }
 public override void InitFrame(UnsafeColor *ptr)
 {
 }
예제 #14
0
 public unsafe void RenderLine(UnsafeColor *ptr, int canvasWidth, int x, int y, char[] chars, int count)
 {
     RenderLine(ptr, canvasWidth, x, y, chars, count, UnsafeColor.WHITE);
 }
예제 #15
0
 public unsafe void RenderLine(UnsafeColor *ptr, int canvasWidth, int x, int y, char[] chars, int count, UnsafeColor color)
 {
     RenderLine(ptr + (y * canvasWidth) + x, canvasWidth, chars, count, color);
 }
예제 #16
0
 public override unsafe void InitFrame(UnsafeColor *ptr)
 {
     Fill(ptr, color);
 }
예제 #17
0
 public override unsafe void RenderFrame(UnsafeColor *ptr)
 {
 }