I have been trying to update a canvas containing a polyline from a
non-UI thread. I have read that in order to do so you have to use the
"Dispatcher".
So, after reading all over the internet about the
dispatcher, I tried it out, but I still get the same exception: I can't
access the objects because the thread doesn't own them.
Here is my XAML:
<Window x:Class="FrequencyAnalysis.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="500" Width="640">
<Grid Background="LightGray">
<Button Content="Play Audio" Height="30" Width="100" Click="Button_Click"
HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="5, 5, 0, 0"></Button>
<Canvas Name="GraphingCanvas" HorizontalAlignment="Center" VerticalAlignment="Center"
Width="500" Height="350" Background="White">
<Polyline Name="poly_line" Stroke="Red" StrokeThickness="2"></Polyline>
</Canvas>
</Grid>
</Window>
And here is my code:
namespace FrequencyAnalysis |
{ |
public partial class Window1 : Window |
{ |
Player MyAudioPlayer; |
XAudio2 audio_device; |
MasteringVoice mastering_voice; |
SourceVoice source_voice; |
WaveFormat wave_format; |
AudioBuffer audio_buffer; |
|
public Window1() |
{ |
InitializeComponent(); |
MyAudioPlayer = new Player(); |
audio_device = new XAudio2(); |
mastering_voice = new MasteringVoice(audio_device); |
source_voice = null; |
wave_format = null; |
} |
|
private void Button_Click(object sender, RoutedEventArgs e) |
{ |
Thread other_thread = new Thread(new ThreadStart(LoopCycle)); |
other_thread.Start(); |
} |
|
delegate void UpdateTheUI(PointCollection point_collection); |
|
private void DrawGraph(float[] data) |
{ |
PointCollection point_collection = new PointCollection(); |
|
for (int x = 0; x < data.Length; x++) |
{ |
point_collection.Add(new Point(data[x], x)); |
} |
//The error happens here!!!!
|
GraphingCanvas.Dispatcher.BeginInvoke((UpdateTheUI)delegate(PointCollection points) |
{ |
poly_line.Points = points; |
}, System.Windows.Threading.DispatcherPriority.Normal, point_collection ); |
} |
|
private void LoopCycle() |
{ |
string FileName = "MusicSurround.wav"; |
|
audio_buffer = MyAudioPlayer.OpenFile(ref audio_device, ref source_voice, ref wave_format, FileName); |
MyAudioPlayer.PlayAudio(ref source_voice, ref audio_buffer); |
|
// loop until the sound is done playing |
while (source_voice.State.BuffersQueued > 0) |
{ |
int sample_count = wave_format.SamplesPerSecond / 100; |
sample_count = NearestPowerOfTwo(sample_count); |
|
Complex[] freq_data = MyAudioPlayer.GetCurrentFrequencies(ref source_voice, ref audio_buffer, |
ref wave_format, sample_count); |
float[] powerspec = FindPowerSpectrum(freq_data); |
|
DrawGraph(powerspec); |
|
Thread.Sleep(10); |
} |
|
audio_buffer.Dispose(); |
source_voice.Dispose(); |
} |
} |
} |
The error clearly happens inside the DrawGraph() function when I try to
assign the new point collection to the polyline. I know that because
everything runs well when that line is commented out.
In case you are having trouble following the code, the functions are:
Button_Click( )
DrawGraph( )
LoopCycle( )
When
the button is clicked, it starts up another thread, and that thread
executes the LoopCycle() function which plays an audio file.
As
the audio file plays, my objective is to output the current frequencies
being played from the audio file in real time (using the FFT). So I
calculate all that jazz using the FFT, and then I want to display my
results (keep in mind this will be happening several times every
second).
Thus I have a very primitive graphing mechanism right
now: a canvas and a polyline. The polyline is composed of all the
points in the resulting data returned from the FFT algorithm. So I
call DrawGraph() which is supposed to hand that set of points to the
polyline object, and then the polyline object should update itself on
the canvas.
Thanks for any help!