MonoTouch: Playing sound - c#

I am trying to play a short sound when user taps on the specific button. But the problem is that I always receive Object reference not set to an instance object. means Null!
I first tried MonoTouch.AudioToolBox.SystemSound.
MonoTouch.AudioToolbox.AudioSession.Initialize();
MonoTouch.AudioToolbox.AudioSession.Category = MonoTouch.AudioToolbox.AudioSessionCategory.MediaPlayback;
MonoTouch.AudioToolbox.AudioSession.SetActive(true);
var t = MonoTouch.AudioToolbox.SystemSound.FromFile("click.mp3");
t.PlaySystemSound();
Let me notice that "click.mp3" is in my root solution folder and it is flagged as Content.
The other approach is MonoTouch.AVFoundation.AVAudioPlayer.
var url = NSUrl.FromFilename("click.mp3");
AVAudioPlayer player = AVAudioPlayer.FromUrl(url);
player.FinishedPlaying += (sender, e) => { player.Dispose(); };
player.Play();
But same error. I googled it and I see that many people has this problem. We need to know whether it is a bug or not.

About using SystemSound and MP3 see this question and answer: Playing a Sound With Monotouch
For AVAudioPlayer be aware that the following pattern is dangerous:
AVAudioPlayer player = AVAudioPlayer.FromUrl(url);
player.FinishedPlaying += (sender, e) => { player.Dispose(); };
player.Play();
since Play is asynchronous. This means the managed player instance can get out of scope before FinishedPlaying occurs. In turn this out of scope means the the GC could already have collected the instance.
A way to fix this is to promote the player local variable into a type field. That will ensure the GC won't collect the instance while the sound is playing.

Your code looks correct (I compared to the code here, which is able to play audio).
What might be the problem is that the audio file isn't included in the app bundle somehow. You can easily check it with this code:
if (!System.IO.File.Exists ("click.mp3"))
Console.WriteLine ("bundling error");

In most cases it would be the File does not exist. If you are like me, and you made sure that the file exists. Ensure the following:
The Path of the file should be relative to your Class (ie: Sounds\beep.wav) (Absolute path did not work for me on the simulator)
Ensure that you are defining the SoundSystem in the class level. This is because MT has a ver agressive Garbage Collector and could dispose your SoundSystem before it even starts playing. see this question

Related

What is wrong with my spawnspots?

I'm trying to make a multiplayer game and in my program there is a spawnSpots code that I made and it's saying that it doesn't exist but it clearly does.
Here's the code:
void Start()
{
spawnSpots = GameObject.FindObjectOfType<SpawnSpot>();
}
Then there is:
if(spawnSpots == null)
{
Debug.LogError("No Spawns Placed.");
return;
}
SpawnSpot mySpawnSpot = spawnSpots[Random.Range (0, spawnSpots.length)];
So everytime the word spawSpots is written, the error is saying:
Severity Code Description Project File Line Suppression State
Error CS0103 The name 'spawnSpots' does not exist in the current context Multiplayer-FPS.CSharp C:\Users\desha\Documents\Multiplayer-FPS\Assets\NetworkManager.cs 15 Active
How can I fix this?
Ok if you are setting up a networked multiplayer game then you will need to have a NetworkStartPosition component on each of your spawn point in the scene.
It is also worth noting that the NetworkManager you would need in your scene also has some spawn settings. Including a spawn method, which if you select "round robin" will randomly spawn players at each of your NetworkStartPositions for you.
Hope this helps.
Here is a link to an excellent video covering the basics of network multiplayer. It is by far the most useful I have found. UnityNetworkTutorial
You are using GameObject.FindObjectOfType incorrectly, it looks like you want to set up an array then pick a random one. Lookup arrays, how to add to the array.
find object of type: https://docs.unity3d.com/ScriptReference/Object.FindObjectOfType.html

Trouble stopping particle emission in unity

I am trying to only allow the particle system to emit particles when something is visible. The particle system knows when to start if a Boolean named avail is true. The code I thought would work for this is the following:
if (avail)
{
GetComponent<MeshRenderer>().enabled = true;
GetComponent<ParticleSystem>().enableEmission = true;
print("Mesh enabled");
}
However, this failed. I also tried:
if (avail)
{
GetComponent<MeshRenderer>().enabled = true;
GetComponent<ParticleSystem>().emission.enabled = true;
print("Mesh enabled");
}
However, this too failed. On every site I have searched in, these two "solutions" came up but they don't work for me. the first example said "this method of doing this is obsolete" and the second example says I can't set "emission.enabled" to a variable because it is a getter not a setter. Any help with figuring this out is extremely appreciated.
I don't have unity opened right now, but I think that
GetComponent<ParticleSystem>().Stop();
is what you need. You can restart the system using
GetComponent<ParticleSystem>().Play();
Also, if you do this often, you should consider keeping your particle system in a class variable.

Song doesn't play all the time in Monogame

Heey,
We created a game with Monogame but we got the following problem.
We got a themesong that plays when you have loaded the game now is the problem that the themesong sometimes plays but sometimes just doesn't. We convert it by the XNA pipeline to a wma and load it into our game with the .xnb together but just sometimes the music doesn't wanna start.
We just use the standard code for starting a song and all of this code does fire.
internal void PlayBackgroundMusic()
{
if (MediaPlayer.GameHasControl)
{
MediaPlayer.Volume = mainVolume;
MediaPlayer.Play(backgroundMusic);
MediaPlayer.IsRepeating = true;
}
}
We also use SoundEffects but these work 100% of the time it's only the song that won't play everytime you start. Windows RT runs it fine by the way.
Make sure that the debugger gets into the if statement through debugging (or remove the statement temporarily). Another possibility might be that the function is running before the game is fully initialized. You could try delaying the function until the game has been fully loaded.
PS: I can't comment on questions yet so here's an answer.
Edit:
Alright, after some messing around with the Song class and looking in the implementation in MonoGame I came to the conclusion that the SoundEffect class is easier to use and better implemented.
backgroundSound = Content.Load<SoundEffect>("alarm");
protected override void BeginRun()
{
// I created an instance here but you should keep track of this variable
// in order to stop it when you want.
var backSong = backgroundSound.CreateInstance();
backSong.IsLooped = true;
backSong.Play();
base.BeginRun();
}
I used this post: using BeginRun override to play the SoundEffect on startup.
If you want to play a song, use the Song class. But make sure you are using ".mp3" instead of ".wma" before converting them into ".xnb".
backgroundMusic = Content.Load<Song>("MainTheme");
MediaPlayer.Play(backgroundMusic);
MediaPlayer.IsRepeating = true;
See MSDN.

Handle system folders event in windows

I am writing some C# code and I need to detect if a specific folder on my windows file system has been opened while the application is running. Is there any way to do it? WinAPI maybe?
There are three API things I think you should check out:
FindFirstChangeNotification() http://msdn.microsoft.com/en-us/library/aa364417%28VS.85%29.aspx
That gives you a handle you can wait on and use to find changes to a file in a particular file, directory, or tree of directories. It won't tell you when a directory is browsed, but it will tell you when a file is saved, renamed, and so on and so forth.
SetWindowsHookEx() http://msdn.microsoft.com/en-us/library/ms644990%28v=VS.85%29.aspx
You can set that up to give you a callback when any number of events occur - in fact I'm pretty positive that you CAN get this callback when a directory is opened, but it will probably be inordinately difficult because you'll be intercepting messages to explorer's window. So you'll be rebooting during debugging.
Windows Shells http://msdn.microsoft.com/en-us/library/bb776778%28v=VS.85%29.aspx
If that wasn't painful enough, you can try writing a shell program.
If you're trying to write a rootkit, I suppose you don't want me to spoil the details for you. If you're NOT trying to write a rootkit, I suggest you look it up - carefully. There are open source rootkits, and they all basically have to monitor file access this way to hide from the user / OS.
Go with the Windows Shell Extensions. You can use Shell Namespace Extensions to make a "virtual" folder that isn't there (or hides a real one), like the GAC (C:\Windows\assembly)
Here are several examples of Shell Extension coding in .Net 4.0.
A Column Handler would let you know when a folder is "Opened", and even let you provide extra data for each of the files (new details columns).
Check out the FileSystemWatcher class.
The closest thing that I can think of, that may be useful to you, is using the static Directory class. It provides methods to determine the last time a file or directory was accessed. You could setup a BackgroundWorker to monitor if the directory was accessed during a specified interval. Keep track of the start and end of the interval by using DateTime, and if the last access time falls between those, then you can use the BackgroundWorker's ProgressChanged event to notify the application.
BackgroundWorker folderWorker = new BackgroundWorker();
folderWorker.WorkerReportsProgress = true;
folderWorker.WorkerSupportsCancellation = true;
folderWorker.DoWork += FolderWorker_DoWork;
folderWorker.ProgressChanged += FolderWorker_ProgressChanged;
folderWorker.RunWorkerAsync();
void FolderWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = (BackgroundWorker)sender;
while(!worker.CancellationPending)
{
DateTime lastAccess = Directory.GetLastAccessTime(DIRECTORY_PATH);
//Check to see if lastAccess falls between the last time the loop started
//and came to end.
if(/*your check*/)
{
object state; //Modify this if you need to send back data.
worker.ReportProgress(0, state);
}
}
}
void FolderWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//Take action here from the worker.ReportProgress being invoked.
}
You could use the FileSystemInfo's LastAccessProperty. The problem though is that it can be cached.
FileSystemInfo: http://msdn.microsoft.com/en-us/library/975xhcs9.aspx
LastAccessTime Property: http://msdn.microsoft.com/en-us/library/system.io.filesysteminfo.lastaccesstimeutc.aspx
As noted that this can be pre-cached.
"The value of the LastAccessTimeUtc property is pre-cached if the current instance of the FileSystemInfo object was returned from any of the following DirectoryInfo methods:
GetDirectories
GetFiles
GetFileSystemInfos
EnumerateDirectories
EnumerateFiles
EnumerateFileSystemInfos
To get the latest value, call the Refresh method."
Therefore call the Refresh method but it still might not be up to date due to Windows caching the value. (This is according to msdn doc "FileSystemInfo.Refresh takes a snapshot of the file from the current file system. Refresh cannot correct the underlying file system even if the file system returns incorrect or outdated information. This can happen on platforms such as Windows 98." - link: http://msdn.microsoft.com/en-us/library/system.io.filesysteminfo.refresh.aspx
I think the only way you can reliably achieve this is by monitoring the currently running processes and watch closely for new Explorer.exe instances and/or new Explorer.exe spawned threads (the "Run every window on a separate process" setting gets in the way here).
I admit I don't have a clue about how to code this, but that's what I would look for.

How can I update the frame of a Windows Media Player COM?

I have a windows media player COM in my Windows Form project that plays and opens videos admirably. However, I would like to be able to grab the first frame of the loaded video so my program users can preview the video (and, ideally, recognize one video from another).
How can I update the frame displayed by the windows media player object?
I have tried using the following code at the end of my openFileDialog event response:
private void openFileDialog1_FileOk(object sender, CancelEventArgs e)
{
Text = openFileDialog1.SafeFileName + " - MPlayer 2.0";
//mediaPlayer1.openPlayer(openFileDialog1.FileName);
mediaPlayer1.URL = openFileDialog1.FileName;
//hopefully, this will load the first frame.
mediaPlayer1.Ctlcontrols.play();
mediaPlayer1.Ctlcontrols.pause();
}
However, when I run this, the pause command gets ignored (Auto-play for video loading is turned off, so the video won't start playing without calling .play(), above). If I had to guess, I'd say that this is because of some threading operation that calls play, moves on to call pause, calls pause, and then, finally, the play resolves, and the video starts - but because the .pause resolved before the .play, the net effect is the .pause is ultimately unheeded.
Firstly, is there a way other than .play(); .pause(); to snag a preview image of the video for the AxWindowsMediaPlayer object? If not, how can I make sure that my .pause() doesn't get ignored?
(I know that .play(); .pause(); works in the general case, because I tested with a separate button that invoked those two methods after the video finished loading, and it worked as expected)
You can't do a lot of things with this COM. However Follow this link and you will find a Class that will help you extract an image from a Video file. You could simply get extract the image and just put it in top of the video, or next to it. This is a simple workaround for your requirement. If not happy with it, I would strongly recommend not using this COM at all and use some other open source video player/plugins. There a lot of real good ones, but I could recommend the VLC Plugin, or try finding another.
Good luck in your quest.
Hanlet
While the Windows Media Player Com might not officially support a feature like this, its quite easy to 'fake' this. If you use a CtlControls2, you have access to the built-in "step(1)" function, which proceeds exactly one frame.
I've discovered that if you call step(1) after calling pause(), searching on the trackbar will also update the video.
It's not pretty, but it works!
This is a little trick to solve the common step(-1) not working issue.
IWMPControls2 Ctlcontrols2 = (IWMPControls2)WindowsMediaPlayer.Ctlcontrols;
double frameRate = WindowsMediaPlayer.network.encodedFrameRate;
Console.WriteLine("FRAMERATE: " + frameRate); //Debug
double step = 1.0 / frameRate;
Console.WriteLine("STEP: " + step); //Debug
WindowsMediaPlayer.Ctlcontrols.currentPosition -= step; //Go backwards
WindowsMediaPlayer.Ctlcontrols.pause();
Ctlcontrols2.step(1);

Categories

Resources