none
Create 'histogram' of xbox controller thumbsticks and triggers in real time. RRS feed

  • Question

  • Hello,

    I am learning c#. I am writing a application that uses the Xbox controller, a mouse, or a finger as an input device. I am focusing on the xbox controller at the moment and polling the controller every 50ms.

    What I would like to do is cteate a simple time series histogram of the inputs from the controller in real time. Specifically the triggers. they return values between 0.00 and 1.So I just need a way to create a graph where x is time since start and y is the input and have it draw across the screen in real time.

    My application is a WPF application. I am looking for general guidance on whether I should be trying to use productivity based elements like charts or livechart or if I need to look into a more 3d or visualization based approach.

    thanks.

     

    Wednesday, December 11, 2019 12:22 AM

All replies

  • Hi aweaver6142,

    Thank you for posting here.

    Since this thread is related to Xbox, I suggest that you can ask this question in the XboxLiveDevelopment forum.

    The Visual C# forum discusses and asks questions about the C# programming language, IDE, libraries, samples, and tools.

    Best Regards,

    Timon


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Wednesday, December 11, 2019 7:52 AM
  • Its not at all related to Xbox. Its a WPF C# app that uses the xbox controller as an input device.
    Wednesday, December 11, 2019 9:46 PM
  • Its not at all related to Xbox. Its a WPF C# app that uses the xbox controller as an input device.

    awe,

    I think the Chart control is good. Seems there is a verson for WPF.

    https://www.codeproject.com/Articles/117673/WPF-Charting-using-MVVM-Pattern

    Here are some histograms using the chart made from bitmap data.

    https://social.msdn.microsoft.com/Forums/vstudio/en-US/619d8583-ffcc-4a25-bbe3-e6c4f5574ed1/how-to-normalize-rgb-value-that-represent-in-histogram?forum=vbgeneral

    One could draw their own graphics but the chart is easy if that is what you want.

    Wednesday, December 11, 2019 10:22 PM
  • Cool I will check it out I just need a way to create a bar every 50ms in real time until the user stops it.
    Wednesday, December 11, 2019 10:42 PM
  • Cool I will check it out I just need a way to create a bar every 50ms in real time until the user stops it.

    awe,

    Do you want just one bar? That is the same as the draw rectangle in the other question? You have a timer loop and update the drawing every tick.

    If you want sixteen bars possibly left and right, labels titles axis grids 3d bars callouts etc then the chart control is easier. You still have a timer and update it with your data...

    Show a picture of what you want if you can.

    http://cfile25.uf.tistory.com/image/144C15114B7239E64D1282

    Why are you using WPF and you should also ask in that forum.

    Wednesday, December 11, 2019 11:22 PM
  • I really just want a cursor that travels to the right along a horizontal line and at defined intervals creates a visual representation of the level of the input. The best example I can think of for a similar effect is any program that records live audio. So I guess it is just a peak meter but instead of showing one sample, it would show all of the samples (vertically). I also want it to record the "reading at each interval" in x,y where X is either the interval number or the number of milliseconds since the recording began and Y is the level of input (between 0 and 1).

    Eventually I would like to make it work with a mouse or a finger on a touch display but since its so easy to poll the input from the triggers on an xbox controller i figured why not start there.

    Thursday, December 12, 2019 1:59 AM
  • I really just want a cursor that travels to the right along a horizontal line and at defined intervals creates a visual representation of the level of the input. The best example I can think of for a similar effect is any program that records live audio. So I guess it is just a peak meter but instead of showing one sample, it would show all of the samples (vertically). I also want it to record the "reading at each interval" in x,y where X is either the interval number or the number of milliseconds since the recording began and Y is the level of input (between 0 and 1).

    Eventually I would like to make it work with a mouse or a finger on a touch display but since its so easy to poll the input from the triggers on an xbox controller i figured why not start there.

    awe,

    Ok then you mean like this ... when the button is down you see the red bar.

    I still don't see where or why the WPF is coming into the project?

    Here is an example that makes the controls. To make the example just copy and paste to an empty form. Change the name as required.

    See how the red bar appears when the mouse is down on the button (green circle in animation only)?

    //mouse tapper animation v2
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Windows.Forms;
    
    namespace Test_C
    {
        public partial class Form6 : Form
        {
            private static System.Timers.Timer Timer1;
            private PictureBox pictureBox1 = new PictureBox();
            private Button button1 = new Button();
            private int MouseDownStatus;
            private List<int> ValuesList = new List<int>();
    
            public Form6()
            {
                InitializeComponent();
            }
    
            private void Form6_Load(object sender, EventArgs e)
            {
                ClientSize = new Size(300, 300);
                Location = new Point(20, 20);
    
                pictureBox1.Location = new Point(10, 20);
                pictureBox1.Size = new Size(280, 80);
                pictureBox1.BackColor = Color.White;
                pictureBox1.Paint += new PaintEventHandler(this.pictureBox1_Paint);
                this.Controls.Add(pictureBox1);
    
                button1.Location = new Point(150, 200);
                button1.Text = "Go";
                this.button1.MouseDown += new MouseEventHandler(this.button1_MouseDown);
                this.button1.MouseUp += new MouseEventHandler(this.button1_MouseUp);
                this.Controls.Add(button1);
    
                Timer1 = new System.Timers.Timer(30);
                Timer1.Elapsed += OnTimedEvent;
                Timer1.Enabled = true;
            }
    
            private void pictureBox1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
            {
                e.Graphics.ScaleTransform(System.Convert.ToSingle(pictureBox1.ClientSize.Width / (double)80), 1);
                e.Graphics.TranslateTransform(0, System.Convert.ToSingle(pictureBox1.ClientSize.Height / (double)2));
                e.Graphics.DrawLine(Pens.Gray, 0, 0, pictureBox1.ClientSize.Width, 0);
    
                for (int x = 0; x <= ValuesList.Count - 1; x++)
                {
                    // draw value red bar
                    e.Graphics.FillRectangle(Brushes.Red, new Rectangle(x, -30, 2, ValuesList[x]));
                    // grid tick
                    e.Graphics.DrawLine(Pens.Black, x, 2, x, -2);
                }
            }
    
            private void button1_MouseDown(object sender, MouseEventArgs e)
            {
                MouseDownStatus = 30;
            }
    
            private void button1_MouseUp(object sender, MouseEventArgs e)
            {
                MouseDownStatus = 0;
            }
    
            private void OnTimedEvent(Object source, System.Timers.ElapsedEventArgs e)
            {
                if (ValuesList.Count > 60) ValuesList.RemoveAt(0);
    
                ValuesList.Add(MouseDownStatus);
    
                pictureBox1.Invalidate();
            }
        }
    }




    • Edited by tommytwotrain Thursday, December 12, 2019 5:00 PM optimized
    Thursday, December 12, 2019 11:17 AM
  • It's a WPF desktop application. This is just one of the parts of it that I am currently working on.
    Thursday, December 12, 2019 9:17 PM
  • Thanks, that will at least give me an idea.

    Here is that code working in WPF just for reference:

    using System; using System.Collections.Generic; using System.Drawing; using System.Windows; using System.Windows.Forms; using System.Windows.Input; namespace Thingy { public partial class Thing : Window { private static System.Timers.Timer Timer1; PictureBox pictureBox1 = new PictureBox(); private int MouseDownStatus; private List<int> ValuesList = new List<int>(); public Freestyle() { InitializeComponent(); } private void pictureBox1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { e.Graphics.ScaleTransform(System.Convert.ToSingle(pictureBox1.ClientSize.Width / (double)80), 1); e.Graphics.TranslateTransform(0, System.Convert.ToSingle(pictureBox1.ClientSize.Height / (double)2)); e.Graphics.DrawLine(Pens.Gray, 0, 0, pictureBox1.ClientSize.Width, 0); for (int x = 0; x <= ValuesList.Count - 1; x++) { e.Graphics.FillRectangle(Brushes.Red, new System.Drawing.Rectangle(x, -30, 2, ValuesList[x])); e.Graphics.DrawLine(Pens.Black, x, 2, x, -2); } } private void OnTimedEvent(Object source, System.Timers.ElapsedEventArgs e) { if (ValuesList.Count > 60) ValuesList.RemoveAt(0); ValuesList.Add(MouseDownStatus); pictureBox1.Invalidate(); } private void button1_MouseUp_1(object sender, MouseButtonEventArgs e) { MouseDownStatus = 0; } private void button1_MouseDown_1(object sender, MouseButtonEventArgs e) { MouseDownStatus = 30; } private void Window_Loaded(object sender, RoutedEventArgs e) { wfhc.Child = pictureBox1; pictureBox1.Location = new System.Drawing.Point(10, 20); pictureBox1.Size = new System.Drawing.Size(280, 80); pictureBox1.BackColor = Color.White; pictureBox1.Paint += new PaintEventHandler(this.pictureBox1_Paint); Timer1 = new System.Timers.Timer(30); Timer1.Elapsed += OnTimedEvent; Timer1.Enabled = true; } } }

    and the XAML

    Window x:Class="Thingy.Thing"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:Thingy"
            xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
            mc:Ignorable="d"
            Loaded="Window_Loaded"
            
            Title="Thing" Height="450" Width="800">
        <Grid>
            <Button x:Name="button1" Content="Button" HorizontalAlignment="Left" MouseUp="button1_MouseUp_1" MouseDown="button1_MouseDown_1" Margin="349,206,0,0" VerticalAlignment="Top" Width="75"/>
            <WindowsFormsHost x:Name="wfhc" HorizontalAlignment="Left" Height="92" Margin="155,89,0,0" VerticalAlignment="Top" Width="461" RenderTransformOrigin="-0.132,0.198"/>
    
    
        </Grid>
    </Window>
    


    Thursday, December 12, 2019 10:14 PM
  • Hi, sorry to bother you this is probably something silly I am doing but when I pass 0 - 100 to x it looks like this: 

    As you can see it's drawing the rectangles under the bar instead of originating them at the bar. 

    Its still a pretty cool effect it's just backwards for some reason, lol =)


    Friday, December 13, 2019 12:19 AM
  • Hi, sorry to bother you this is probably something silly I am doing but when I pass 0 - 100 to x it looks like this: 

    As you can see it's drawing the rectangles under the bar instead of originating them at the bar. 

    Its still a pretty cool effect it's just backwards for some reason, lol =)


    Yes well I am not sure why without seeing the code.

    Note there is the TranslateTransform which moves the y origin = 0 to the center of the picturebox height. Then the red rect has a y value of -30. Recall the y values increase from top to bottom. So -30 is above the center line. Do you have positive values then they will plot below the line?

    Play with that. If you cant find it post your code. I am not sure why I did it that way but I am sure it shows up if you set a break on the fillrect line and examine the values.

    You can make that look many ways. You could even use a chart control to get more fancy. If you have some other ideas or example to go by of how you want it to look you can draw most anyway you want it if that is the basic thing you want...


    Friday, December 13, 2019 10:59 AM
  •  I am gonna work on it a bit this weekend (party :)) I would like to see if I could find a way to do it without the WindowsFormHost (i.e. find a replacement control for picturebox that can draw on a canvas or other WPF control) I will check into breaking the fillrect line to see if I can get more visual clues as to why its drawing that way because I would like to understand more about visual elements.
    using NAudio.CoreAudioApi;
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Windows;
    using System.Windows.Forms;
    using System.Windows.Threading;
    namespace Thingy
    {
        public partial class Thing : Window
        {
            private static System.Timers.Timer Timer1;
            PictureBox pictureBox1 = new PictureBox();
            private int peakint;
            private List<int> ValuesList = new List<int>();
            MMDeviceEnumerator pEnumerator = new MMDeviceEnumerator();
            MMDevice pDevice = null;
            AudioMeterInformation pMeterInfo = null;
            DispatcherTimer fsAudioTimer = new DispatcherTimer();
            private float peak;
            public Thing()
            {
                InitializeComponent();
            }
            private void pictureBox1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
            {
                e.Graphics.ScaleTransform(System.Convert.ToSingle(pictureBox1.ClientSize.Width / (double)80), 1);
                e.Graphics.TranslateTransform(0, System.Convert.ToSingle(pictureBox1.ClientSize.Height / (double)2));
                e.Graphics.DrawLine(Pens.Gray, 0, 0, pictureBox1.ClientSize.Width, 0);
                for (int x = 0; x <= ValuesList.Count - 1; x++)
                {
                    // draw value red bar
                    e.Graphics.FillRectangle(Brushes.Red, new Rectangle(x, -30, 2, ValuesList[x]));
                    // grid tick
                    e.Graphics.DrawLine(Pens.Black, x, 2, x, -2);
                }
            }
            private void OnTimedEvent(Object source, System.Timers.ElapsedEventArgs e)
            {
                if (ValuesList.Count > 60) ValuesList.RemoveAt(0);
                pDevice = pEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Console);
                pMeterInfo = pDevice.AudioMeterInformation;
                float peak = pMeterInfo.MasterPeakValue;
                //to get an int should be 0-100
                peakint = (int) (peak * 100);
                ValuesList.Add(peakint);
                pictureBox1.Invalidate();
            }
            private void Window_Loaded(object sender, RoutedEventArgs e)
            {
                wfhc.Child = pictureBox1;
                pictureBox1.Location = new System.Drawing.Point(20, 10);
                pictureBox1.Size = new System.Drawing.Size(280, 80);
                pictureBox1.BackColor = Color.White;
                pictureBox1.Paint += new PaintEventHandler(this.pictureBox1_Paint);
                Timer1 = new System.Timers.Timer(30);
                Timer1.Elapsed += OnTimedEvent;
                Timer1.Enabled = true;
            }
        }
    }
    

    <Window x:Class="Thingy.Thing"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:Thingy"
            xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
            mc:Ignorable="d"
            Loaded="Window_Loaded"
            
            Title="Thing" Height="450" Width="800">
        <Grid>
            <WindowsFormsHost x:Name="wfhc" HorizontalAlignment="Left" Height="191" Margin="155,10,0,0" VerticalAlignment="Top" Width="461"/>
            <Canvas x:Name="canvas" HorizontalAlignment="Left" Height="152" Margin="155,229,0,0" VerticalAlignment="Top" Width="461"/>
        </Grid>
    </Window>
    
    Right now I am just feeding it audio playing on Spotify as input just because I want to see how it behaves with randomness.

    • Edited by aweaver6142 Friday, December 13, 2019 9:14 PM
    Friday, December 13, 2019 9:05 PM
  • awe,

    As I had mentioned since you are using a range of values from 0 to 100 you need to offset the y values by that amount for the red bars to appear above the x axis line. The example has offset -30 in the example I posted change it to 100 ie

      e.Graphics.FillRectangle(Brushes.Red, new Rectangle(x, -100, 2, ValuesList[x]));
                

    If you make that 0 instead of -100 then your will draw the values below the xaxis 0. If you use -50 you will see values on both sides of the x axis etc. Play with the offset value and watch what happens.

    PS those are pixels ie 100 pixels so you may need to adjust for the size of the window etc. You can set up a scale like I did for the x-axis etc.
    Friday, December 13, 2019 11:09 PM