Threading issue with MediaPlayer C# - c#

I am creating an app that has an 9x9 multidimensional array full of custom objects, and when a button is pressed a sound specific to every single object needs to be played.
I run this method to load the sound, runs with no known problems:
public void setSoundToPlay()
{
OpenFileDialog openFileDiag = new OpenFileDialog();
if ((bool)openFileDiag.ShowDialog())
{
audio = new MediaPlayer();
audio.Open(new System.Uri(openFileDiag.FileName));
}
}
but when I activate another method to play sound:
public void buttonActivated()
{
audio.Play();
}
I get a System.InvalidOperationException:{"The calling thread cannot access this object because a different thread owns it."}
The object running the mediaplayer object is nested within another object, is that the problem. I tried understanding threading but have made no headway.
I also need to, in some cases, need to have all the sounds be able to play at once. Is this the best object for the job?

Use the Dispatcher property associated with the media player element, like this
public void buttonActivated()
{
audio.Dispatcher.Invoke(() =>
{
audio.Play();
});
}

To access devices like this from other methods use
Application.Current.Dispatcher.BeginInvoke(new Action(() => objectName);

Related

Music suddenly stops playing in C# Windows Form application

In my app there should normally be 2 sounds that overlap (a song and some short sounds). It works fine until from time to time the song suddenly stops playing. Do you know how I could solve this issue?
Here are the functions that I used for adding the songs and the sounds:
private void musical()
{
var p1 = new System.Windows.Media.MediaPlayer();
p1.Open(new System.Uri(#"J:\penalty\penalty\penalty\bin\Debug\Avicii - The Nights (Lyrics HD).wav"));
p1.Play();
}
private void happy()
{
System.Threading.Thread.Sleep(500);
var p2 = new System.Windows.Media.MediaPlayer();
p2.Open(new System.Uri(#"J:\penalty\penalty\penalty\bin\Debug\happy.wav"));
p2.Play();
}
private void sad()
{
System.Threading.Thread.Sleep(500);
var p2 = new System.Windows.Media.MediaPlayer();
p2.Open(new System.Uri(#"J:\penalty\penalty\penalty\bin\Debug\sad.wav"));
p2.Play();
}
If you don't store a reference to the MediaPlayers as field but only in local variables they are likely going to be gargabe collected. This might happen delayed as MediaPlayer uses unmanaged resources that would require a Dispose before being freed. To fix your problem consider storing a reference to the MediaPlayers in a field.
private System.Windows.Media.MediaPlayer _music;
private System.Windows.Media.MediaPlayer _happy;
private System.Windows.Media.MediaPlayer _sad;
and then adjust your methods to use the field instead.
private void musical()
{
_music = new System.Windows.Media.MediaPlayer();
_music.Open(new System.Uri(#"J:\penalty\penalty\penalty\bin\Debug\Avicii - The Nights (Lyrics HD).wav"));
_music.Play();
}
If you would like to repeat the audio (which does not work very well) you can subscribe the MediaEnded event in order set the position back to the beginning and start playing again.

Load heavy user control with laoding animation without freezing app

so after more than a week of trying to solve it on my own I officially give up and turn to your help. Basically, it should not be so complicated so I have no idea why it does not work. I have a WPF app which contains a Main Window called surprise surpise...: Main_Window.
That window contain a user control called 'pageTransitionControl' that change its content according to what the client want to see. the 'pageTransitionControl' is there to support multiple animations and so on... Anyway, among all of the user controls, i have a preety havy uc called ucBanks. before it shows, the ucBanks load a lot of data, manipulating it and display it on a very beautiful and smart charts. the problem is it takes some time to load it, approximately 6-7 seconds so i need the UI to show 'Loading' animation during that time (another user control called 'ucSpinner').
I'm Trying to load the ucBanks on a different thread to avoid freezing the application and it works great: the ucSpinner is showed immidiatlly and the ucBanks is loading on the background but when i change the content of the 'pageTransitionControl' i get this error:
"The calling thread cannot access this object because a different thread owns it".
I think i tried basically everything but i must missing somthing or doing somthing wrong.
This is where it all start, the btn_click event that load ucBanks:
ShowSpinner();
Thread.Sleep(100);
Thread newThread = new Thread(new ThreadStart(LoadUc));
newThread.SetApartmentState(ApartmentState.STA);
newThread.IsBackground = true;
newThread.Start();
This is the ShowSpinner method:
private void ShowSpinner()
{
ucSpinner.Opacity = 1;
}
and this is the LoadUc method:
private void LoadUc()
{
ucOsh ucOshx = new ucOsh();
Utils.LoadUc(ucOshx, null, PageTransitions.PageTransitionType.GrowAndFade, true, this, null, true);
}
With the LoadUc i called static class called 'Utils' holding the 'LoadUc' method:
public static void LoadUc(System.Windows.Controls.UserControl ucParent, System.Windows.Controls.UserControl ucChild, PageTransitions.PageTransitionType tranType, bool removeChildrens = true, System.Windows.Window w = null, List<Plist.Plist> lst = null, bool hideMenu = false)
{
MainWindow win = null;
if (w != null) { win = (MainWindow)w; }
else { win = (MainWindow)System.Windows.Window.GetWindow(ucChild); }
win.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.ContextIdle, (System.Action)delegate
{
win.pageTransitionControl.TransitionType = tranType;
win.pageTransitionControl.PARAMS = lst;
win.pageTransitionControl.Tag = ucParent.ToString();
win.pageTransitionControl.pages.Push(ucParent);
win.pageTransitionControl.Content = ucParent; ----------->>>>This is where i get the error!!!
});
}
I understand that the main window is locked inside another thread but i cant see any other option to load it without freezing the entire app.
Does anyone have a suloution to my problem? SA :-) ?
What I have tried:
i tried working with background-worker, i chaned all of the settings of the dispatcher, loaded the user control inside and outside the threads...

Moving Between One Form to Another - C#

I am currently creating a video game and coding the movement in a house between upstairs and downstairs. I'm using PictureBoxes in combination with an IntersectWith event to transition between forms.
Transition Code to go Upstairs:
if(picPlayer.Bounds.IntersectsWith(picUpstairsTransition.Bounds))
{
MapFrmHouseUpstairs upstairs = new MapFrmHouseUpstairs();
this.Hide();
upstairs.ShowDialog();
}
Transition Code to Go Back Downstairs:
if(picPlayer.Bounds.IntersectsWith(picGoDownstairs.Bounds))
{
MapFrmHouse goDownstairs = new MapFrmHouse();
this.Hide();
goDownstairs.ShowDialog();
picPlayer.Location = new Point(497, 103);
}
The issue I have is that when the player enters the house, he starts at the front. When he tries to come back from upstairs, the character is moved back to the front instead of the base of the stairs. Is there anyway I could create a method within MapFrmHouse such as:
public void fromDownstairs{picPlayer.Location = new Point(x,y);}
And call it when going downstairs?
First of all you'd better use a game engine to design games like Unity which is much easier and more fun. This might be a learning task for you though which I don't blame :)
You can send information between forms in Windows Forms in many ways, two of which seems to fulfill your needs:
Using a higher level modifier for objects
Initializing values in constructors
To do the first you set the modifier property of the picture box called picPlayer in your MapFrmHouse class from private to public then you can do exactly what you mentioned:
if(picPlayer.Bounds.IntersectsWith(picGoDownstairs.Bounds))
{
MapFrmHouse goDownstairs = new MapFrmHouse()
{
picPlayer.Location = new Point(x,y);
};
this.Hide();
goDownstairs.ShowDialog();
}
For the second method you should create an overload constructor in your MapFrmHouse and accept a Point value in it and then set it your value like this:
public MapFrmHouse(Point p)
{
InitializeComponent();
picPlayer.Location = p;
}
And then use it in your code like this:
if(picPlayer.Bounds.IntersectsWith(picGoDownstairs.Bounds))
{
MapFrmHouse goDownstairs = new MapFrmHouse(new Point(x,y));
this.Hide();
goDownstairs.ShowDialog();
}
I hope this helps :)

How to notify UI about action done in API?

I'm writing a simple game, like tic tac toe (only mine is bigger). The game play is simple: my turn, check if won, bot's turn, check if won. I have simple UI and API that uses Domain Entites (that's not important now). So when user's moves, API will update the board, will know that next move is bot's move, so will do it and... has to notify UI. Here is my problem.
My question is:
How to notify UI about bot's move? I mean, to keep it simple but stick to the best programming practices.
My first thought was to create an event in GameAPI class. Is that good idea? Today will all new stuff, C# 6, etc.. I'm not sure:/ Right now UI is WinForms, but I would like to use this API in other UIs, like WPF or even mobile. Here is my simplified code of UI:
EDIT: Right now I'm talking about single player game. Both UI and API is a Client. There will be multiplayer through central server in next step, but right now, single player.
public partial class Form1 : Form
{
private GameAPI api;
public Form1()
{
InitializeComponent();
api = new GameAPI();
}
private void boardClick(object sender, EventArgs e)
{
Field field = GetClickedField(e);
MoveResult result = api.MakeMove(clickedColumn);
if (result != null && result.Row >= 0)
{
MessageBox.Show(result.Row + "," + clickedColumn);
if (result.IsConnected)
{
MessageBox.Show("Success!");
}
}
}
}
and API:
public class GameAPI
{
public IGame CurrentGame { get; set; }
public void CreateGame(GameType type)
{
CurrentGame = new SinglePlayerGame();
}
public Result Move(int column)
{
if (CurrentGame == null) return null;
Player player = CurrentGame.GetNextPlayer();
if (player.Type == PlayerType.Human) return CurrentGame.Move(column, player.Id);
}
public Result MoveBot()
{
// Simulate Bot's move...
}
}
My first thought was to create an event in GameAPI class. Is that good idea?
Yes, why not? Let take for example the modern UI frameworks data binding. The key point of making data binging work is providing a property change notification (read - event) when some property value of the object is modified. Usually that's implemented via IPropertyNotifyChanged interface, which is simply a polymorphic way of declaring support for PropertyChanged event. This way, if you set the object property via code, the UI updates automatically. Hope you see the similarity with your case - the API does something and raises an event, UI (being attached handler to that event as some earlier point) receives the event and updates accordingly.

What is the correct pattern to use when multiple events can cause a calculation?

I've been having a lot of annoying problems with accessing bitmaps lately and I'm starting to think I need to re-evaluate the design of my application.
At present, I have one object that creates two others. One of these others provides a series of bitmaps (e.g. from a webcam) and the other provides a series of coordinate pairs (e.g. from a mouse or touchpad). The parent object has listeners for the events that the children objects generate. The events are have custom arguments which carry the new information (bitmap or coordinates) as a payload. For instance:
public class NewImageEventArgs : EventArgs
{
public NewImageEventArgs(Bitmap image)
{
Image = image;
}
public Bitmap Image { get; private set; }
}
The listener methods in the parent object copy the payload to the object-level representation. When either listener (bitmap or coordinates) is triggered, they subsequently call a shared method, which uses both the bitmap and the coordinates to do some calculation.
private void PointerModule_NewPosition(object sender, NewPositionEventArgs e)
{
this.p = e.Position;
this.Invalidated();
}
It seems to me that my recurring problems with OutOfMemory and InvalidOperation ("Object is currently in use elsewhere") exceptions stem from the fact that each new event may be on a different thread. When a bitmap is being used on one thread, exceptions are raised when another thread tries to simultaneously access it.
Do I need to fundamentally change the shape of my program? Are there any precautions I can take to eliminate this type of problem?
EDIT:
I reread your question and I might have responded to a slightly different question than you've asked. However I fought the same problems with "Object is used elsewhere" exceptions and the lesson I learned is summarized in the link in the bottom. Neither bitmap.Clone() nor new Bitmap(source) creates deep image copy. This is causing the exceptions.
In my application I use pattern :
public class ImageProvider()
{
public event EventHandler LiveImageUpdated;
private object _currentImageLock = new object();
private Bitmap _currentImage;
public Bitmap CurrentImage
{
get
{
lock (_currentImageLock)
{
return DeepImageCopy(_currentImage)
}
}
private set
{
lock(_currentImageLock)
{
_currentImage = value
}
if (LiveImageUpdated != null)
{
foreach (Delegate del in LiveImageUpdated.GetInvocationList())
{
EventHandler handler = (EventHandler)del;
handler.BeginInvoke(this, EventArgs.Empty, null, null);
}
}
}
}
}
And I asked this question :
How to create a Bitmap deep copy
Application works without a problem. So basically I skipped the part with pushing new image as an argument and listeners are asking for deep copy of the image and they are not sharing images. Performancewise it works without a problem.

Categories

Resources