Drawing audio waveform in winforms using OpenAL and VC++ - winforms

I have a project on my studies about signal processing and I'm obliged to record sound from some source (e.g. microphone) and display the waveform and its FFT on two charts. I can't provide the functions displaying the buffer into chart and not stalling the whole program.
As I don't know C# yet, I chose Winforms under C++/CLI and OpenAL for sound capturing (0 experience in sound processing and this was recommended in few threads and the rest of my team is working with it so I'm kind of obliged to use it). I already have the code for chosing the sound device and recording sound into a buffer. I know that the while loop below is stalling the programm. I was thinking of using multi-thread but no idea how to use it. Or events...
This is code for capture start, inside some button event.
int go = 0;
const int SRATE = 44100;
const int SSIZE = 1024;
ALCdevice *hydromike;
ALbyte buffer[22050];
ALint sample;
some_event {
const char* diwajs = (msclr::interop::marshal_as<std::string>(item)).c_str();
hydromike = alcCaptureOpenDevice(diwajs, SRATE, AL_FORMAT_MONO16, SSIZE);
alcCaptureStart(hydromike);
buffering(); //function call for starting the buffer
}
void buffering(void)
{
while (go) {
alcGetIntegerv(hydromike, ALC_CAPTURE_SAMPLES, (ALCsizei)sizeof(ALint), &sample);
alcCaptureSamples(hydromike, (ALCvoid *)buffer, sample);
}
}
void drw(void)
{
chart1->Series["Bufor"]->Points->Clear();
for (int i = 0; i < 22050; i++)
{
chart1->Series["Bufor"]->Points->AddXY(i, (int)buffer[i]);
}
}
I would like it to work like this: https://www.youtube.com/watch?v=q9cRZuosrOs

I managed to complete the code, the problem was the UI and OpenAL threads were interfering with each other which resulted in freezing the app. Starting the buffering in another thread and setting a semaphore resolved the problem.
static Semaphore^ drawingSem = gcnew Semaphore(1, 1);
Thread^ oThread;
alcCaptureStart(hydromike);
oThread = gcnew Thread(gcnew ThreadStart(buffering));
oThread->Start();
static void buffering()
{
while (go) {
drawingSem->WaitOne();
alcGetIntegerv(hydromike, ALC_CAPTURE_SAMPLES, (ALCsizei)sizeof(ALint), buffer);
alcCaptureSamples(hydromike, (ALCvoid *)buffer, sample*2);
drawingSem->Release();
}
}
void MyForm::drw()
{
drawingSem->WaitOne();
chart1->Series["Bufor"]->Points->Clear();
for (int i = 0; i < bufsize; i++)
{
chart1->Series["Bufor"]->Points->AddXY(i, (int)buffer[i]);
}
drawingSem->Release();
}
private: System::Void timer1_Tick(System::Object^ sender, System::EventArgs^ e) {
drw();
}

Related

How to create an interrupt or background task in C

I am trying to figure out how to make a background process in C, I haven't found anything online. I think the reason I haven't found much is that I may not be asking the right question.
I am working on a school project using ARM assembly and C with Raspberry Pi using the Raspbian Operating System and the GCC compiler.
For my project, I decided to do a simple alarm system. The idea is simple, I have 3 PIR sensors that detect motion, and set off a buzzer when motion is detected.
Instead of using hardware for the keypad, I chose to do a GUI where you can arm/disarm the system as well as change the password and turn on/off certain sensors. For example, if you wanted to turn off motion sensors 1 and 2, there will be buttons on the GUI to do that.
The problem I am having is that the GUI Library I'm using, (GTK3 and Glade) is event-driven, so unless a button is pressed it won't do anything. This would be fine if I only wanted to set the buzzer to on when motion is detected because I could make my own loop to watch for motion, but I really want to play some type of pattern repeatedly (example: 3 short beeps continuously until the alarm is disarmed). At the same time, I still want to have access to the GUI buttons so I can turn off the alarm. This makes me think I need an interrupt or a background process.
I'm sure there are many ways to solve this problem, but I really wanted to create sort of a background routine that could run parallel to the main task once the user's arms' the system. I have asked my professor about how to multi-thread in ARM, but he said that we do not cover this in the class. I have read that the C standard library doesn't support multi-threading, but I am sure there is a way that this can be done. If someone could just point me in the right direction I would be grateful.
Here is the GUI code I have written, along with another module I have finished to get a working prototype before I start wiring the sensors. None of this contains any assembly code because I haven't got to that yet, I wanted to get it working at a higher level and then break certain parts down to assembly. If you need all of the files I will gladly provide them, but I think this should be enough for you all to understand what I'm trying to do.
The GUI code:
/*****************************************************
* Title: User Interface
* Author:
* Date: 2/15/20
* Class: CISP 2410
*
* Description:
* ! This header file contains all of the function
* ! Declarations and definitions for the GUI
* ! Everything involving the GUI will be in
* --> this header
*
*****************************************************/
#include <gtk/gtk.h> // For gtk stuff
#include <stdlib.h> // Standard Library
#include "GUI_Ctrl.h" // Prototypes
#include "Alarm.h" // Alarm Functions
/* Private Functions */
static void UpdateLcdDisplay(char keyPadValue);
static void ClearLcdDisplay(void);
static void UpdateDisplay(void);
/***********************************/
// Global Variables
GtkWidget *g_Lbl_Lcd;
GtkWidget *g_Lbl_SysStat;
GtkWidget *g_Lbl_LogStat;
char inputBuffer[6] = {0x0};
int charCounter = 0;
const int INPUT_LIMIT = 6;
/*****************************************************
* ! Initializes all of the GUI elements
* ! Adds CSS attributes to GUI elements
* ! Also handles the formatting of text
*****************************************************/
void GUI_INIT(int argc, char* argv[])
{
GtkBuilder *builder;
GtkWidget *window;
GtkWidget *g_Lbl_Lbl1;
GtkWidget *g_Lbl_Lbl2;
// Get pointer to css file
GtkCssProvider *cssProvider = gtk_css_provider_new ();
gtk_css_provider_load_from_path(cssProvider,"UI/style.css",NULL);
gtk_init(&argc, &argv);
// get pointer to xml dat file
builder = gtk_builder_new_from_file("UI/HelloWorld.glade");
// Get pointer to main window
window = GTK_WIDGET(gtk_builder_get_object(builder, "window_main"));
gtk_builder_connect_signals(builder, NULL);
// Get pointer to labels
g_Lbl_Lcd = GTK_WIDGET(gtk_builder_get_object(builder, "Lbl_Lcd"));
g_Lbl_SysStat = GTK_WIDGET(gtk_builder_get_object(builder, "Lbl_SysStat"));
g_Lbl_LogStat = GTK_WIDGET(gtk_builder_get_object(builder, "Lbl_LogStat"));
g_Lbl_Lbl1 = GTK_WIDGET(gtk_builder_get_object(builder, "Lbl1"));
g_Lbl_Lbl2 = GTK_WIDGET(gtk_builder_get_object(builder, "Lbl2"));
// Align Text Right
gtk_label_set_xalign (GTK_LABEL(g_Lbl_Lcd), 1);
// Align Text Left
gtk_label_set_xalign (GTK_LABEL(g_Lbl_SysStat), 0);
gtk_label_set_xalign (GTK_LABEL(g_Lbl_LogStat), 0);
gtk_label_set_xalign (GTK_LABEL(g_Lbl_Lbl1), 0);
gtk_label_set_xalign (GTK_LABEL(g_Lbl_Lbl2), 0);
// Add CSS
gtk_style_context_add_provider_for_screen(gdk_screen_get_default(), GTK_STYLE_PROVIDER(cssProvider), GTK_STYLE_PROVIDER_PRIORITY_USER);
// Finish setup and start GUI
g_object_unref(builder);
gtk_widget_show(window);
UpdateDisplay();
gtk_main();
return;
}
// called when window is closed
void on_window_main_destroy()
{
gtk_main_quit();
}
/*------------------END SECTION------------------*/
static void UpdateDisplay(void)
{
ClearLcdDisplay();
gtk_label_set_text(GTK_LABEL(g_Lbl_SysStat), GetSysStat());
gtk_label_set_text(GTK_LABEL(g_Lbl_LogStat), GetLogStat());
return;
}
static void UpdateLcdDisplay(char keyPadValue)
{
if (charCounter >= INPUT_LIMIT)
ClearLcdDisplay();
else
{
inputBuffer[charCounter] = keyPadValue;
charCounter++;
}
gtk_label_set_text(GTK_LABEL(g_Lbl_Lcd), inputBuffer);
return;
}
static void ClearLcdDisplay(void)
{
memset(inputBuffer, 0, 6);
charCounter = 0;
gtk_label_set_text(GTK_LABEL(g_Lbl_Lcd), inputBuffer);
return;
}
/*------------------ EVENT HANDLERS ------------------*/
void on_btn0_clicked(void) {UpdateLcdDisplay('0');}
void on_btn1_clicked(void) {UpdateLcdDisplay('1');}
void on_btn2_clicked(void) {UpdateLcdDisplay('2');}
void on_btn3_clicked(void) {UpdateLcdDisplay('3');}
void on_btn4_clicked(void) {UpdateLcdDisplay('4');}
void on_btn5_clicked(void) {UpdateLcdDisplay('5');}
void on_btn6_clicked(void) {UpdateLcdDisplay('6');}
void on_btn7_clicked(void) {UpdateLcdDisplay('7');}
void on_btn8_clicked(void) {UpdateLcdDisplay('8');}
void on_btn9_clicked(void) {UpdateLcdDisplay('9');}
void on_btnClear_clicked(void) {ClearLcdDisplay();}
void on_btnEnter_clicked(void) {UpdateDisplay();}
void on_btnLogin_clicked(void)
{
LoginLogout(inputBuffer);
UpdateDisplay();
return;
}
void on_btnArm_clicked(void)
{
ArmDisarm();
UpdateDisplay();
return;
}
Here is the code for the alarm. Note, this is just a prototype, I will be using the wiringPi library once I get this interrupt thing figured out.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Alarm.h"
// Private Function Prototypes
static int PasswordIsCorrect(char* pswAttempt);
// Global Variables
static int armed = 0;
static int loggedIn = 0;
static int PasswordIsCorrect(char* pswAttempt)
{
const char PASSWORD[] = "123";
if (!strcmp(pswAttempt, PASSWORD))
return 1;
else
return 0;
}
/*
* Returns a string,
* Used to update label
*/
char* GetSysStat(void)
{
if(armed)
return "Armed";
else
return "Disarmed";
}
/*
* Returns a string,
* Used to update label
*/
char* GetLogStat(void)
{
if(loggedIn)
return "Logged In";
else
return "Logged Out";
}
void ArmDisarm(void)
{
if (loggedIn && armed)
armed = 0;
else if(loggedIn && !armed)
armed = 1;
return;
}
void LoginLogout(char* pswAttempt)
{
if(PasswordIsCorrect(pswAttempt))
loggedIn = 1;
else
loggedIn = 0;
return;
}
Thank you all for your time!

Why does focusing a TextBox speed up another thread's progress?

Hello Stackoverflowers!
I have a strange situation here. I wrote a small C# tool, to measure the time it takes to send a request via usb and wait until the response arrives. The communication is no problem, nor are the devices.
I put a simple TextBox on the GUI together with a "Start" button.
Pressing "Start" starts a thread with a loop (loops defined in the TextBox) sending requests and receiving the answers. In the GUI thread you can see a progress bar showing the status.
When I pressed "Start" I measured around 15 ms per command and response (in average) which is quite a long time.
When I pressed "Start" and clicked inside the TextBox (only setting the cursor inside), the worker thread ran through with an average time of 1.6 ms per command and response.
This is reproducible in my case. I even put in a new, totally useless TextBox. When clicking inside, the thread speeds up, when I focus another element, like a slider, the thread slows down again.
Has anyone ever heard about something like that and can explain to me, why this happens? And even better, how I can avoid this behavior?
FYI:
- I tried both BackgroundWorker and Thread, same behavior.
- For taking the times, I'm using .NET's stopwatch.
- Executing the measurement method in the GUI thread changes nothing. EDIT: Tried again and everything's as it should be. But now I have the problem, that the GUI freezes, since I'm working in its thread.
- Removing the progress bar changes nothing.
Thanks a lot!
EDIT:
private void tbAmount_Copy_PreviewKeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
if (String.IsNullOrEmpty(tbDevAddrTime.Text))
return;
pbTimeMeasure.Value = 0;
worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(StartTimeMeasureHandler);
worker.RunWorkerAsync();
}
}
private void StartTimeMeasure()
{
int address = 1;
int loops = 1;
DispatchIfNecessary(() =>
{
address = Convert.ToInt32(tbDevAddrTime.Text);
loops = Convert.ToInt32(tbAmount_Copy.Text);
pbTimeMeasure.Maximum = loops;
});
System.Diagnostics.Stopwatch stopwatchLong = new System.Diagnostics.Stopwatch();
System.Diagnostics.Stopwatch stopwatchShort = new System.Diagnostics.Stopwatch();
var cmd = String.Format(":{0:X3}4010200300000000\r", RequestAddress + address);
for (int i = 0; i < loops; i++)
{
stopwatchLong.Start();
//SND
_canbus.Write(cmd, cmd.Length, ref _bytesWritten);
stopwatchShort.Start();
Thread.Sleep(1);
//RCV
var answer = ReadContent();
stopwatchShort.Stop();
stopwatchLong.Stop();
DispatchIfNecessary(() =>
{
pbTimeMeasure.Value++;
});
}
Double resLong = Convert.ToDouble(stopwatchLong.ElapsedMilliseconds);
Double resLongAvg = resLong / loops;
Double resShort = Convert.ToDouble(stopwatchShort.ElapsedMilliseconds);
Double resShortAvg = resShort / loops;
DispatchIfNecessary(() =>
{
lbTotalResLong.Content = resLong.ToString();
lbTotalResShort.Content = resShort.ToString();
int length = 5;
if (resLongAvg.ToString().Length < 5)
{
length = resLong.ToString().Length;
}
if (resShortAvg.ToString().Length < length)
{
length = resShortAvg.ToString().Length;
}
lbAvgResLong.Content = resLongAvg.ToString().Substring(0,length);
lbAvgResShort.Content = resShortAvg.ToString().Substring(0,length);
});
}

why is GlutPostRedisplay and sleep function is not working in this code?

i have tried to implement the data transfer between usb and cpu in this project. The data transfer is being shown as a small rectangle moving from one component of the computer to another.
In the code below, the GlutPostRedisplay does not work.
Also, can someone tell me if sleep() used is correct because the functions called in display do not work in sync. casing() is never executed. After fisrtscreen(), it directly jumps to opened() and operate() does not work.
what is the error with this code ??
void operate()
{
URLTEXTX = 200;
URLTEXTY = 950;
displayString(READUSB,1);
//southbrigde to northbrigde
bottom(488.0,425.0,380.0);
back(488.0,188.0,380.0);
top(188.0,380.0,550.0);
//northbridge to cpu
front(230.0,350.0,595.0);
top(345.0,600.0,650.0);
//read from usb
back(700.0,625.0,465.0);
bottom(625.0,460.0,385.0);
back(620.0,525.0,390.0);
sleep(1);
URLTEXTX = 200;
URLTEXTY = 950;
displayString(WRITEUSB,1);
//cpu to northbridge
bottom(350.0,650.0,595.0);
back(350.0,230.0,600.0);
//northbridge to southbridge
bottom(188.0,550.0,380.0);
front(188.0,488.0,380.0);
top(483.0,380.0,425.0);
//write to usb
front(525.0,625.0,385.0);
top(625.0,385.0,460.0);
front(620.0,700.0,460.0);
sleep(1);
URLTEXTX = 200;
URLTEXTY = 950;
displayString(READDVD,1);
//read from dvd
back(600.0,560.0,810.0);
bottom(570.0,810.0,600.0);
back(560.0,525.0,610.0);
//ram to northbridge
back(450.0,230.0,580.0);
//northbridge to cpu
front(230.0,350.0,595.0);
top(345.0,600.0,650.0);
sleep(1);
URLTEXTX = 200;
URLTEXTY = 950;
displayString(WRITEDVD,1);
//cpu to northbridge
bottom(350.0,650.0,595.0);
back(350.0,230.0,600.0);
//northbridge to ram
front(230.0,450.0,580.0);
//write to dvd
front(525.0,570.0,600.0);
top(570.0,600.0,800.0);
front(560.0,600.0,800.0);
sleep(1);
URLTEXTX = 200;
URLTEXTY = 950;
displayString(READHD,1);
//read from hard disc
back(640.0,560.0,300.0);
top(560.0,300.0,530.0);
back(560.0,525.0,530.0);
//ram to northbridge
back(450.0,230.0,580.0);
//northbridge to cpu
front(230.0,350.0,595.0);
top(345.0,600.0,650.0);
sleep(1);
URLTEXTX = 200;
URLTEXTY = 950;
displayString(WRITEHD,1);
//cpu to northbridge
bottom(350.0,650.0,595.0);
back(350.0,230.0,600.0);
//northbridge to ram
front(230.0,450.0,580.0);
//write to hard disc
front(525.0,560.0,530.0);
bottom(560.0,530.0,300.0);
front(560.0,640.0,300.0);
sleep(1);
}
void front(GLfloat x1,GLfloat x2,GLfloat y1)//to move in forward direction
{
GLfloat i;
for(i=x1;i<=x2;i++)
{
drawbit(i,x1+5,y1,y1-5);
glutPostRedisplay();
}
}
void back(GLfloat x1,GLfloat x2,GLfloat y1)//to move in backward direction
{
GLfloat i;
for(i=x1;i>=x2;i--)
{
drawbit(i,i-5,y1,y1-5);
glutPostRedisplay();
}
}
void top(GLfloat x1,GLfloat y1,GLfloat y2)//to move in upward direction
{
GLfloat i;
for(i=y1;i<=y2;i++)
{
drawbit(x1,x1+5,i,i+5);
glutPostRedisplay();
}
}
void bottom(GLfloat x1,GLfloat y1,GLfloat y2)//to move in downward direction
{
GLfloat i;
for(i=y1;i>=y2;i--)
{
drawbit(x1,x1-5,i,i-5);
glutPostRedisplay();
}
}
void drawbit(GLfloat x1,GLfloat x2,GLfloat y1,GLfloat y2)
{
glBegin(GL_POLYGON);
glColor3f(1.0,1.0,1.0);
glVertex2f(x1,y1);
glVertex2f(x2,y1);
glVertex2f(x2,y2);
glVertex2f(x1,y2);
glEnd();
glFlush();
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
firstscreen(); //introduction to the project
sleep(3);
glClear(GL_COLOR_BUFFER_BIT);
casing(); //cpu case
sleep(2);
glClear(GL_COLOR_BUFFER_BIT);
opened(); //when cpu case is opened shows internal components
sleep(1);
operate(); //data transfer between various components
}
The problem is similar to this: Pausing in OpenGL successively
glutPostRedisplay simply sets a flag in glut to call your display callback on the next loop. It doesn't actually draw anything.
The function I suspect you're after is glutSwapBuffers. Without double buffering, geometry is drawn directly to the screen (although "draw" commands to the GPU are buffered for which you'd want glFlush). This commonly causes flickering because you see things that later get covered by closer geometry (because of the depth buffer). Double buffering solves this by rendering to an off-screen buffer and then displaying the result all at once. Make sure GLUT_DOUBLE is passed to glutInit so that you have a back buffer.
While you're sleep()ing, your application won't be able to capture and process events. Lets say you want to close the window. Until sleep returns the whole thing will be unresponsive. A sleep can still be important so you don't hog your CPU. I'd separate these concepts.
Loop/poll with an idle function until your delay time has elapsed. Then call glutPostRedisplay. Add glutSwapBuffers to display if you're double buffering.
Write a framerate limiter that calls sleep so you don't hog cycles.
A simple method to draw different things after set delays is to write a small state machine...
int state = STATE_INIT;
float timer = 0.0f;
void idle()
{
//insert framerate limiter here
//calculate time since last frame, perhaps using glutGet(GLUT_ELAPSED_TIME)
float deltaTime = ...
timer -= deltaTime;
if (timer < 0.0f)
{
switch (state)
{
case STATE_INIT:
state = STATE_DRAW_FIRST_THING;
timer = 123.0f;
...
}
glutPostRedisplay();
}
}
void display()
{
...
if (state == STATE_DRAW_FIRST_THING)
{
...
}
...
glutSwapBuffers();
}
As your app becomes bigger this I doubt this will be maintainable and you'll want something more robust, but until then this is a good start.
Simply changing a void (*currentView)(void); callback function in idle would save some hard coding in display. You might want to create an object orientated state machine. Beyond boolean states you might want to look into animation and keyframe interpolation. Rather than hard code everything, storing geometry, keyframes and state sequences in a file is a nice way to separate code and data. XML is very nice to work with for this provided you use a library.

How make a per-pixel output on WPF/XNA?

I want to draw like in the old qbasik, where you can into 5 lines and PSET (x, y) derive any graph, or Lissajous figures.
Question: what better way to go for WPF? and way for XNA?
Any samples?
For WPF and Silverlight
WriteableBitmap
http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.writeablebitmap.aspx
WriteableBitmapEx library. Tries to compensate that with extensions methods that are easy to use like built in methods and offer GDI+ like functionality:
http://writeablebitmapex.codeplex.com/
In XNA this isn't the most efficient thing in general, but I think your best bet is to probably create a texture and set each pixel using SetData, and render it to the screen with SpriteBatch.
SpriteBatch spriteBatch;
Texture2D t;
Color[] blankScreen;
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
//initialize texture
t = new Texture2D(GraphicsDevice, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height, false, SurfaceFormat.Color);
//clear screen initially
blankScreen = new Color[GraphicsDevice.Viewport.Width * GraphicsDevice.Viewport.Height];
for (int i = 0; i < blankScreen.Length; i++)
{
blankScreen[i] = Color.Black;
}
ClearScreen();
}
private void Set(int x, int y, Color c)
{
Color[] cArray = { c };
//unset texture from device
GraphicsDevice.Textures[0] = null;
t.SetData<Color>(0, new Rectangle(x, y, 1, 1), cArray, 0, 1);
//reset
GraphicsDevice.Textures[0] = t;
}
private void ClearScreen()
{
//unset texture from device
GraphicsDevice.Textures[0] = null;
t.SetData<Color>(blankScreen);
//reset
GraphicsDevice.Textures[0] = t;
}
protected override void Draw(GameTime gameTime)
{
spriteBatch.Begin();
spriteBatch.Draw(t, Vector2.Zero, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
With this you can call either Set or ClearScreen at will in your Update or Draw. You may have to play with the texture index (I just used 0 for this example, might not be it for you), and also you only need to unset / reset one time per frame, so you can optimize that depending on how you use them.

C# Winforms: BeginInvoke still running on same thread?

I'm web developer and I'm trying to step into multithreading programming.
On one form I'm trying to run a method computing values in a second thread using asynchronous delegates.
I also want a progress bar showing actual progress in UI thread been notified.
delegate void ShowProgressDelegate(int total, int value);
delegate void ComputeDelegate(int value);
//Some method simulating sophisticated computing process
private void Compute(int value)
{
ShowProgress(value, 0);
for (int i = 0; i <= value; i++)
{
ShowProgress(value, i);
}
}
//Method returning values into UI thread
private void ShowProgress(int total, int value)
{
if (!this.InvokeRequired)
{
ComputeButton.Text = value.ToString();
ProgressBar.Maximum = total;
ProgressBar.Value = value;
}
else
{
ShowProgressDelegate showDel = new ShowProgressDelegate(ShowProgress);
this.BeginInvoke(showDel, new object[] { total, value });
}
}
//firing all process
private void ComputeButton_Click(object sender, EventArgs e)
{
ComputeButton.Text = "0";
ComputeDelegate compDel = new ComputeDelegate(Compute);
compDel.BeginInvoke(100000, null, null);
}
When I run this, everything is computing without any problem except it is still running in UI thread (I suppose so, because it freezes when I click some button on the form).
Why? I also attach buildable sample project (VS2010) with same code: http://osmera.com/windowsformsapplication1.zip
Thanks for helping neewbie.
In the code you've shown, you're doing nothing other than updating the progress bar - so there are thousands of UI messages to marshal, but nothing significant happening in the non-UI thread.
If you start simulating real work in Compute, you'll see it behave more reasonably, I suspect. You need to make sure you don't swamp the UI thread with progress updates like you are doing now.

Resources