Reputation: 609
I've tried to avoid asking what some seem to see as trivial questions on stackoverflow. Last time I asked a question I got lots of negative responses, so I thought I'd try to figure this one out on my own. So probably about one month, 2 books, and some video tutorials later, i'm still very stumped. :)
Line 39 of my MainWindow.xaml.cs class gets called according to my debugger, but note 30 or 31 don't seem to trigger anything on the UI, at one point it did, but it also gave me a run-time error. after weeks of being stumped, I kind of took a break and moved onto other things, so i'm not exactly sure what I did to get rid of the run-time error. So, now I'm asking for help, please :)
UPDATE
The exception returned on line 45 of MainWindow.xaml.cs :
"object because a different thread owns it."
My MIDI Class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NAudio.Midi;
namespace StaveHelper
{
public sealed class MIDIMain
{
private static MIDIMain midiMain = null;
public static int noteOnNumber;
public static int noteOffNumber;
public MidiIn midiIn;
public bool noteOn;
private bool monitoring;
private int midiInDevice;
private MIDIMain()
{
GetMIDIInDevices();
}
public static MIDIMain GetInstance()
{
if (null == midiMain)
{
midiMain = new MIDIMain();
}
return midiMain;
}
public string[] GetMIDIInDevices()
{
//Get a list of devices
string[] returnDevices = new string[MidiIn.NumberOfDevices];
//Get the product name for each device found
for (int device = 0; device < MidiIn.NumberOfDevices; device++)
{
returnDevices[device] = MidiIn.DeviceInfo(device).ProductName;
}
return returnDevices;
}
public void StartMonitoring(int MIDIInDevice)
{
if (midiIn == null)
{
midiIn = new MidiIn(MIDIInDevice);
}
midiIn.Start();
monitoring = true;
}
public void midiIn_MessageReceived(object sender, MidiInMessageEventArgs e)
{
//int noteNumber;
// Exit if the MidiEvent is null or is the AutoSensing command code
if (e.MidiEvent != null && e.MidiEvent.CommandCode == MidiCommandCode.AutoSensing)
{
return;
}
if (e.MidiEvent.CommandCode == MidiCommandCode.NoteOn)
{
// As the Command Code is a NoteOn then we need
// to cast the MidiEvent to the NoteOnEvent
NoteOnEvent ne;
ne = (NoteOnEvent)e.MidiEvent;
noteOnNumber = ne.NoteNumber;
}
if (e.MidiEvent.CommandCode == MidiCommandCode.NoteOff)
{
NoteEvent ne;
ne = (NoteEvent)e.MidiEvent;
noteOffNumber = ne.NoteNumber;
}
}
//// send the note value to the the MainWindow for display
//public int sendNoteNum(int noteNumber)
//{
// noteOnNumber = noteNumber;
// noteOn = true;
// return noteOnNumber;
//}
}
}
My MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using NAudio.Midi;
using System.Threading;
using System.Windows.Threading;
namespace StaveHelper
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
Config config;
MIDIMain midiMain;
public delegate void mon(object sender, MidiInMessageEventArgs e);
public MainWindow()
{
this.InitializeComponent();
// Insert code required on object creation below this point.
midiMain = MIDIMain.GetInstance();
config = new Config();
config.load_MIDIIn_Devices();
//Thread t = new Thread(monitorNotes);
midiMain.midiIn.MessageReceived += new EventHandler<MidiInMessageEventArgs>(monitorNotes);
}
public void monitorNotes(object sender, MidiInMessageEventArgs e) //LINE 39: MONITOR NOTES
{
switch ( MIDIMain.noteOnNumber)
{
case 30:
C3.Opacity = 100; //LINE 45: "The calling thread cannot access this
C3Dot.Opacity = 100; //object because a different thread owns it."
break;
case 31:
D3Dot.Opacity = 100;
break;
}
}
~MainWindow()
{
}
private void btnConfig_Click(object sender, RoutedEventArgs e)
{
config.Show();
}
}
}
So it seems the answer was to change:
switch ( MIDIMain.noteOnNumber)
{
case 30:
C3.Opacity = 100; //LINE 45: "The calling thread cannot access this
C3Dot.Opacity = 100; //object because a different thread owns it."
break;
case 31:
D3Dot.Opacity = 100;
break;
}
}
into
switch ( MIDIMain.noteOnNumber)
{
case 60:
C3.Dispatcher.BeginInvoke(
System.Windows.Threading.DispatcherPriority.Normal,
new System.Windows.Threading.DispatcherOperationCallback
(delegate
{
C3.Opacity = 100;
C3Dot.Opacity = 100;
MIDIMain.noteOffNumber = -1;
return null;
}), null);
break;
case 61:
D3Dot.Dispatcher.BeginInvoke(
System.Windows.Threading.DispatcherPriority.Normal,
new System.Windows.Threading.DispatcherOperationCallback
(delegate
{
D3Dot.Opacity = 100;
D3Dot.Opacity = 100;
MIDIMain.noteOnNumber = -1;
return null;
}), null);
break;
}
Thanks for all of the help!
Upvotes: 3
Views: 846
Reputation: 49492
Your exception is because you are trying to modify WPF GUI components from a background thread. You need to use the Dispatcher. There are lots of questions here on stack overflow that give help on this. For example, you could use the code from this answer
yourControl.Dispatcher.BeginInvoke(
System.Windows.Threading.DispatcherPriority.Normal,
new System.Windows.Threading.DispatcherOperationCallback(delegate
{
// update your GUI here
return null;
}), null);
Upvotes: 2
Reputation: 62524
If you will look in the StartMonitoring()
method - in case when midiIn
is null it creates a new instance and then calling Start()
but in this case nobody subscribed to MessageReceived
so it seems that you forgot to subscribe for midiIn.MessageReceived
event in this case and method midiIn_MessageReceived
is never called. So noteOnNumber
iremains unassigned since only in this method (midiIn_MessageReceived
) I see a code which assigns a value to noteOnNumber
variable.
Try out updating StartMonitoring() method:
if (midiIn == null)
{
midiIn = new MidiIn(MIDIInDevice);
midiIn.MessageReceived += midiIn_MessageReceived;
}
Upvotes: 1