.net c# windows forms, play sound without overlaping [duplicate] - c#

Is there a way to play two sounds at the same time?
I know that SoundPlayer isn't able to do this.
I can't use SoundEffect as I believe it's only part of XNA.
The two required sounds will be called at unknown and random times. The sound needs to be be controlled after it is played. i.e., the sound must be able to be stopped before it has finished playing.

Reference PresentationCore and WindowsBase and try this...
var p1 = new System.Windows.Media.MediaPlayer();
p1.Open(new System.Uri(#"C:\windows\media\tada.wav"));
p1.Play();
// this sleep is here just so you can distinguish the two sounds playing simultaneously
System.Threading.Thread.Sleep(500);
var p2 = new System.Windows.Media.MediaPlayer();
p2.Open(new System.Uri(#"C:\windows\media\tada.wav"));
p2.Play();
EDIT
I received a downvote probably because at first glance this looks like it will play the second sound after the first is finished. It doesn't, they are played by windows asynchronously. The sleep is there so if you test this code verbatim you can hear the sounds play together, it wouldn't be noticeable without the delay since they are the same sound.
This code demonstrates the two sounds playing on separate threads on top of each other, which is sort of pointless since the playback doesn't block anyway
new System.Threading.Thread(() => {
var c = new System.Windows.Media.MediaPlayer();
c.Open(new System.Uri(#"C:\windows\media\tada.wav"));
c.Play();
}).Start();
System.Threading.Thread.Sleep(500);
new System.Threading.Thread(() => {
var c = new System.Windows.Media.MediaPlayer();
c.Open(new System.Uri(#"C:\windows\media\tada.wav"));
c.Play();
}).Start();
http://msdn.microsoft.com/en-us/library/system.windows.media.mediaplayer.stop.aspx
The class also has the control you need to stop playback

The "MediaPlayer" object will not let you play two sounds at once, even if you create two instances. You will need to bring in the native windows API "mciSendString".
[DllImport("winmm.dll")]
static extern Int32 mciSendString(string command, StringBuilder buffer, int bufferSize, IntPtr hwndCallback);
public Form1()
{
InitializeComponent();
mciSendString(#"open C:\Users\Jono\Desktop\applause.wav type waveaudio alias applause", null, 0, IntPtr.Zero);
mciSendString(#"play applause", null, 0, IntPtr.Zero);
mciSendString(#"open C:\Users\Jono\Desktop\foghorn.wav type waveaudio alias foghorn", null, 0, IntPtr.Zero);
mciSendString(#"play foghorn", null, 0, IntPtr.Zero);
}

check PlaySound method here http://msdn.microsoft.com/en-us/library/aa909766.aspx, and its flag SND_ASYNC .

Solution :
Hi,
I was developing a WP8 App and i needed multiple sounds to play simultaneously, the solutions mentioned above didnt work for me, So i used the XNA framwork. here is the link
http://msdn.microsoft.com/en-us/library/ff842408.aspx
and then play ur sound files like this...
SoundEffect Sound = SoundEffect.FromStream(Application.GetResourceStream(new Uri("Assets/Sounds/wav/sound.wav", UriKind.Relative)).Stream);
Sound.Play();
For looping...
SoundEffectInstance Sound = SoundEffect.FromStream(Application.GetResourceStream(new Uri("Assets/Sounds/wav/sound.wav", UriKind.Relative)).Stream).CreateInstance();
Sound.IsLooped = true;
Sound.Play();
Note: the files must be in ".wav" (PCM, 8 or 16-bit, 8KHz to 48KHz, mono or stereo) format

From http://alvas.net/alvas.audio,samples.aspx#sample7 and http://alvas.net/alvas.audio,samples.aspx#sample6
Player pl = new Player();
byte[] arr = File.ReadAllBytes(#"in.wav");
pl.Play(arr);
Player pl2 = new Player();
pl2.FileName = "123.mp3";
pl2.Play();
or mix audio data before playing How to mix to mix two audio file..
private void Mix(string outfile, string infile1, string infile2, int shiftSec)
{
WaveReader wr1 = new WaveReader(File.OpenRead(infile1));
WaveReader wr2 = new WaveReader(File.OpenRead(infile2));
IntPtr format1 = wr1.ReadFormat();
WaveFormat wf = AudioCompressionManager.GetWaveFormat(format1);
WaveWriter ww = new WaveWriter(File.Create(outfile), AudioCompressionManager.FormatBytes(format1));
byte[] data0 = wr1.ReadData(0, shiftSec);
byte[] data1 = wr1.ReadData(shiftSec);
byte[] data2 = wr2.ReadData();
byte[] mixData = AudioCompressionManager.Mix(format1, data2, data1);
ww.WriteData(data0);
ww.WriteData(mixData);
ww.Close();
wr2.Close();
wr1.Close();
}

The accepted answer didn't work for me.
Here is what worked:
Make sure you add references to PresentationCore and WindowsBase dlls.
private void Form1_Load(object sender, EventArgs e)
{
System.Media.SoundPlayer player = new System.Media.SoundPlayer(#"C:\Users\me\source\repos\TacticalIslandSurvival\sounds\blazer rail.wav");
player.PlayLooping();
}
private void button1_MouseEnter(object sender, EventArgs e)
{
var p1 = new System.Windows.Media.MediaPlayer();
p1.Open(new System.Uri(#"C:\Users\me\source\repos\TacticalIslandSurvival\sounds\click.wav"));
p1.Play();
}

Related

Send multiple tone generators to a mixer and play the output of the mixer

Using NAudio Library
I have seen the following link:
https://markheath.net/post/mixing-and-looping-with-naudio (don't fully understand the detail)
I am able to play generated tones one after the other.
What I don't understand is how to combine multiple tone generators/sources into a mixer and play the output of that mixer as shown below:
I'm missing some fundamental understanding of the process here I think, so any pointers or further detail would really help me move forward.
Creating the mixer:
WaveMixerStream32 mixer = new WaveMixerStream32();
mixer.AddInputStream(GenerateUpperSine(args[0]));
mixer.AddInputStream(GenerateLowerSine(args[1]));
Creating the Sine Generators:
private static WaveStream GenerateUpperSine(string frequency){
var outFormat = WaveFormat.CreateIeeeFloatWaveFormat(44100, 1);
const int durationInSecods = 5;
var UpperFreq = new SignalGenerator();
{
Gain = 0.2,
Frequency = Double.Parse(frequency),
Type = SignalGeneratorType.Sin
}
var sp = UpperFreq.ToWaveProvider16();
byte{} data = new Byte[outFormat.AverageBytesPerSecond * durationInSeconds];
var bytesRead = new sp.Read(data, 0, data.Length);
return new RawSourceWaveStream(new MemoryStream(data), outFormat);
}
The GenerateLowerSine function is an exact duplicate of the upper one, just with some variables changed.
Calling this application from the command line as follows:
c:\path\to\exe\my.exe 1000 750
I have some code which I can add to the generation code which plays the tones as expected, one after the other. This may reside within the function and is as follows:
var wo = new WaveOutEvent();
wo.Init(UpperFreq);
wo.Play();
This indicates the basic functionality is working, at least in part.
What I believe I am missing is either:
how to send the output of the tone generators to the mixer and then initiate the playback from the output of the mixer
or
possibly connect the output of the mixer to a WaveOut device and send
the audio/data from the tone generators to the inputs of the mixer
Perhaps it is something else though.
Any help would be greatly appreciated.

SharpDX XAudio2: 6 SourceVoice limit

I have been playing around with SharpDX.XAudio2 for a few days now, and while things have been largely positive (the odd software quirk here and there) the following problem has me completely stuck:
I am working in C# .NET using VS2015.
I am trying to play multiple sounds simultaneously.
To do this, I have made:
- Test.cs: Contains main method
- cSoundEngine.cs: Holds XAudio2, MasteringVoice, and sound management methods.
- VoiceChannel.cs: Holds a SourceVoice, and in future any sfx/ related data.
cSoundEngine:
List<VoiceChannel> sourceVoices;
XAudio2 engine;
MasteringVoice master;
public cSoundEngine()
{
engine = new XAudio2();
master = new MasteringVoice(engine);
sourceVoices = new List<VoiceChannel>();
}
public VoiceChannel AddAndPlaySFX(string filepath, double vol, float pan)
{
/**
* Set up and start SourceVoice
*/
NativeFileStream fileStream = new NativeFileStream(filepath, NativeFileMode.Open, NativeFileAccess.Read);
SoundStream soundStream = new SoundStream(fileStream);
SourceVoice source = new SourceVoice(engine, soundStream.Format);
AudioBuffer audioBuffer = new AudioBuffer()
{
Stream = soundStream.ToDataStream(),
AudioBytes = (int)soundStream.Length,
Flags = SharpDX.XAudio2.BufferFlags.EndOfStream
};
//Make voice wrapper
VoiceChannel voice = new VoiceChannel(source);
sourceVoices.Add(voice);
//Volume
source.SetVolume((float)vol);
//Play sound
source.SubmitSourceBuffer(audioBuffer, soundStream.DecodedPacketsInfo);
source.Start();
return voice;
}
Test.cs:
cSoundEngine engine = new cSoundEngine();
total = 6;
for (int i = 0; i < total; i++)
{
string filepath = System.IO.Directory.GetParent(System.IO.Directory.GetCurrentDirectory()).Parent.FullName + #"\Assets\Planet.wav";
VoiceChannel sfx = engine.AddAndPlaySFX(filepath, 0.1, 0);
}
Console.Read(); //Input anything to end play.
There is currently nothing worth showing in VoiceChannel.cs - it holds 'SourceVoice source' which is the one parameter sent in the constructor!
Everything is fine and well running with up to 5 sounds (total = 5). All you hear is the blissful drone of Planet.wav. Any higher than 5 however causes the console to freeze for ~5 seconds, then close (likely a c++ error which debugger can't handle). Sadly no error message for us to look at or anything.
From testing:
- Will not crash as long as you do not have more than 5 running sourcevoices.
- Changing sample rate does not seem to help.
- Setting inputChannels for master object to a different number makes no difference.
- MasteringVoice seems to say the max number of inputvoices is 64.
- Making each sfx play from a different wav file makes no difference.
- Setting the volume for sourcevoices and/or master makes no difference.
From the XAudio2 API Documentation I found this quote: 'XAudio2 removes the 6-channel limit on multichannel sounds, and supports multichannel audio on any multichannel-capable audio card. The card does not need to be hardware-accelerated.'. This is the closest I have come to finding something that mentions this problem.
I am not well experienced with programming sfx and a lot of this is very new to me, so feel free to call me an idiot where appropriate but please try and explain things in layman terms.
Please, if you have any ideas or answers they would be greatly appreciated!
-Josh
As Chuck has suggested, I have created a databank which holds the .wav data, and I just reference the single data store with each buffer. This has improved the sound limit up to 20 - however this has not fixed the problem as a whole, likely because I have not implemented this properly.
Implementation:
class SoundDataBank
{
/**
* Holds a single byte array for each sound
*/
Dictionary<eSFX, Byte[]> bank;
string curdir => Directory.GetParent(Directory.GetCurrentDirectory()).Parent.FullName;
public SoundDataBank()
{
bank = new Dictionary<eSFX, byte[]>();
bank.Add(eSFX.planet, NativeFile.ReadAllBytes(curdir + #"\Assets\Planet.wav"));
bank.Add(eSFX.base1, NativeFile.ReadAllBytes(curdir + #"\Assets\Base.wav"));
}
public Byte[] GetSoundData(eSFX sfx)
{
byte[] output = bank[sfx];
return output;
}
}
In SoundEngine we create a SoundBank object (initialised in SoundEngine constructor):
SoundDataBank soundBank;
public VoiceChannel AddAndPlaySFXFromStore(eSFX sfx, double vol)
{
/**
* sourcevoice will be automatically added to MasteringVoice and engine in the constructor.
*/
byte[] buffer = soundBank.GetSoundData(sfx);
MemoryStream memoryStream = new MemoryStream(buffer);
SoundStream soundStream = new SoundStream(memoryStream);
SourceVoice source = new SourceVoice(engine, soundStream.Format);
AudioBuffer audioBuffer = new AudioBuffer()
{
Stream = soundStream.ToDataStream(),
AudioBytes = (int)soundStream.Length,
Flags = SharpDX.XAudio2.BufferFlags.EndOfStream
};
//Make voice wrapper
VoiceChannel voice = new VoiceChannel(source, engine, MakeOutputMatrix());
//Volume
source.SetVolume((float)vol);
//Play sound
source.SubmitSourceBuffer(audioBuffer, soundStream.DecodedPacketsInfo);
source.Start();
sourceVoices.Add(voice);
return voice;
}
Following this implementation now lets me play up to 20 sound effects - but NOT because we are playing from the soundbank. Infact, even running the old method for sound effects now gets up to 20 sfx instances.
This has improved up to 20 because we have done NativeFile.ReadAllBytes(curdir + #"\Assets\Base.wav") in the constructor for the SoundBank.
I suspect NativeFile is holding a store of loaded file data, so you regardless of whether you run the original SoundEngine.AddAndPlaySFX() or SoundEngine.AddAndPlaySFXFromStore(), they are both running from memory?
Either way, this has quadrupled the limit from before, so this has been incredibly useful - but requires further work.

BASS WASAPI BPMCounter

I want to analyse my default playback device and detect the beats. I've been using the BASS WASAPI to get the FFT data of the selected device with:
int ret = BassWasapi.BASS_WASAPI_GetData(_fft, (int)BASSData.BASS_DATA_FFT2048);
Now I was using the data to generate spectrum data and display this to the user. In addition I want to detect the Beats using the BPMCounter Class from BASS. However as far as I can tell the BPMCounter.ProcessAudio() function requires a stream (which I don't get with WASAPI) in order to work. Is there a ways I can use BPMCounter with WASAPI? Would be great if someone can point me to the right direction. Thanks
Edit:
Tried this to convert the data to a stream, but without success:
int ret = BassWasapi.BASS_WASAPI_GetData(_fft, (int)BASSData.BASS_DATA_FFT2048); //get channel fft data
var chan = Bass.BASS_StreamCreate(0, 44100, BASSFlag.BASS_DEFAULT, BASSStreamProc.STREAMPROC_PUSH);
Bass.BASS_ChannelPlay(chan, false);
Bass.BASS_StreamPutData(chan, _fft, _fft.Length);
bool beat = _count.ProcessAudio(chan, true);
Debug.Write(beat);
beat is always False, however I can see at the Spectrum that the capturing of the FFT Data is corrent.
I've just started playing with this lib a few hours ago and i am still going through the examples. So my answer maybe is not what you want. For my project i also want to transform WASAPI into a stream and use it for a displaying a spectrum. What i did was to create a StreamPush, right after BASS_WASAPI initialization.
To init your WASAPI use this call and this delegate:
private InitWasapi()
{
WASAPIPROC _process = new WASAPIPROC(Process); // Delegate
bool res = BassWasapi.BASS_WASAPI_Init(_YourDeviceNumber, 0, 0, BASSWASAPIInit.BASS_WASAPI_BUFFER, 1f, 0f, _process, IntPtr.Zero);
if (!res)
{
// Do error checking
}
// This is the part you are looking for (maybe!)
// Use these flags because Wasapi needs 32-bit sample data
var info = BassWasapi.BASS_WASAPI_GetInfo();
_stream = Bass.BASS_StreamCreatePush(info.freq, info.chans, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT, IntPtr.Zero);
BassWasapi.BASS_WASAPI_Start();
}
private int Process(IntPtr buffer, int length, IntPtr user)
{
Bass.BASS_StreamPutData(_stream, buffer, length);
return length;
}
Please note: This works, but i am still experimenting. For example i am not getting the same spectrum output as when i create the stream from the music file itself. There are some (small) differences. Maybe it's because i am using a custom EQ in Winamp for playing the same .mp3. So if anyone knows more on this subject, i would like also to hear it!

Reading WMV resolution in C#

I'm trying to read the image size of a WMV file in C#.
I've tried using what is described here:
How do I get the duration of a video file using C#?
but the only attribute that has a value is Duration.
Any ideas ?
Thanks.
Only way I've seen it done is by playing it and attaching to the open event:
static WindowsMediaPlayerClass player;
static void Main()
{
player = new WindowsMediaPlayerClass();
IWMPMedia mediaInfo = player.newMedia("test.wmv");
player.OpenStateChange += new _WMPOCXEvents_OpenStateChangeEventHandler(player_OpenStateChange);
player.currentMedia = mediaInfo;
//...
Console.WriteLine("Done.");
Console.ReadKey();
}
private static void player_OpenStateChange(int state)
{
if (state == (int)WMPOpenState.wmposMediaOpen)
{
Console.WriteLine( "height = " + player.currentMedia.imageSourceHeight);
Console.WriteLine( "width = " + player.currentMedia.imageSourceWidth);
}
}
You'll want to dispose of any resources before exiting.
You use the code from the linked example, but you explicitly do a function call to get the height and width.
Example:
using WMPLib; // this file is called Interop.WMPLib.dll
WindowsMediaPlayerClass wmp = new WindowsMediaPlayerClass();
IWMPMedia mediaInfo = wmp.newMedia("myfile.wmv");
long height, width;
mediaInfo.get_imageSourceHeight(height);
mediaInfo.get_imageSourceWidth(width);
I prefer to use the free NReco.VideoInfo.dll. Mainly because I hate Windows Media Player. I have found that WMP is unreliable.
Here is the download link: http://www.nrecosite.com/video_info_net.aspx It's useful for other stuff too.
var ffProbe = new NReco.VideoInfo.FFProbe();
var videoInfo = ffProbe.GetMediaInfo(pathToFile);
Int32 tmpHeight = videoInfo.Streams[0].Height;
Int32 tmpWidth = videoInfo.Streams[0].Width;

Best way to play MIDI sounds using C#

I'm trying to rebuild an old metronome application that was originally written using MFC in C++ to be written in .NET using C#. One of the issues I'm running into is playing the midi files that are used to represent the metronome "clicks".
I've found a few articles online about playing MIDI in .NET, but most of them seem to rely on custom libraries that someone has cobbled together and made available. I'm not averse to using these, but I'd rather understand for myself how this is being done, since it seems like it should be a mostly trivial exercise.
So, am I missing something? Or is it just difficult to use MIDI inside of a .NET application?
I'm working on a C# MIDI application at the moment, and the others are right - you need to use p/invoke for this. I'm rolling my own as that seemed more appropriate for the application (I only need a small subset of MIDI functionality), but for your purposes the C# MIDI Toolkit might be a better fit. It is at least the best .NET MIDI library I found, and I searched extensively before starting the project.
I think you'll need to p/invoke out to the windows api to be able to play midi files from .net.
This codeproject article does a good job on explaining how to do this:
vb.net article to play midi files
To rewrite this is c# you'd need the following import statement for mciSendString:
[DllImport("winmm.dll")]
static extern Int32 mciSendString(String command, StringBuilder buffer,
Int32 bufferSize, IntPtr hwndCallback);
Hope this helps - good luck!
midi-dot-net got me up and running in minutes - lightweight and right-sized for my home project. It's also available on GitHub. (Not to be confused with the previously mentioned MIDI.NET, which also looks promising, I just never got around to it.)
Of course NAudio (also mentioned above) has tons of capability, but like the original poster I just wanted to play some notes and quickly read and understand the source code.
I think it's much better to use some library that which has advanced features for MIDI data playback instead of implementing it by your own. For example, with DryWetMIDI (I'm the author of it) to play MIDI file via default synthesizer (Microsoft GS Wavetable Synth):
using Melanchall.DryWetMidi.Devices;
using Melanchall.DryWetMidi.Core;
// ...
var midiFile = MidiFile.Read("Greatest song ever.mid");
using (var outputDevice = OutputDevice.GetByName("Microsoft GS Wavetable Synth"))
{
midiFile.Play(outputDevice);
}
Play will block the calling thread until entire file played. To control playback of a MIDI file, obtain Playback object and use its Start/Stop methods (more details in the Playback article of the library docs):
var playback = midiFile.GetPlayback(outputDevice);
// You can even loop playback and speed it up
playback.Loop = true;
playback.Speed = 2.0;
playback.Start();
// ...
playback.Stop();
// ...
playback.Dispose();
outputDevice.Dispose();
I can't claim to know much about it, but I don't think it's that straightforward - Carl Franklin of DotNetRocks fame has done a fair bit with it - have you seen his DNRTV?
You can use the media player:
using WMPLib;
//...
WindowsMediaPlayer wmp = new WindowsMediaPlayer();
wmp.URL = Path.Combine(Application.StartupPath ,"Resources/mymidi1.mid");
wmp.controls.play();
For extensive MIDI and Wave manipulation in .NET, I think hands down NAudio is the solution (Also available via NuGet).
A recent addition is MIDI.NET that supports Midi Ports, Midi Files and SysEx.
Sorry this question is a little old now, but the following worked for me (somewhat copied from Win32 - Midi looping with MCISendString):
[DllImport("winmm.dll")]
static extern Int32 mciSendString(String command, StringBuilder buffer, Int32 bufferSize, IntPtr hwndCallback);
public static void playMidi(String fileName, String alias)
{
mciSendString("open " + fileName + " type sequencer alias " + alias, new StringBuilder(), 0, new IntPtr());
mciSendString("play " + alias, new StringBuilder(), 0, new IntPtr());
}
public static void stopMidi(String alias)
{
mciSendString("stop " + alias, null, 0, new IntPtr());
mciSendString("close " + alias, null, 0, new IntPtr());
}
A full listing of command strings is given here. The cool part about this is you can just use different things besides sequencer to play different things, say waveaudio for playing .wav files. I can't figure out how to get it to play .mp3 though.
Also, note that the stop and close command must be sent on the same thread that the open and play commands were sent on, otherwise they will have no effect and the file will remain open. For example:
[DllImport("winmm.dll")]
static extern Int32 mciSendString(String command, StringBuilder buffer,
Int32 bufferSize, IntPtr hwndCallback);
public static Dictionary<String, bool> playingMidi = new Dictionary<String, bool>();
public static void PlayMidi(String fileName, String alias)
{
if (playingMidi.ContainsKey(alias))
throw new Exception("Midi with alias '" + alias + "' is already playing");
playingMidi.Add(alias, false);
Thread stoppingThread = new Thread(() => { StartAndStopMidiWithDelay(fileName, alias); });
stoppingThread.Start();
}
public static void StopMidiFromOtherThread(String alias)
{
if (!playingMidi.ContainsKey(alias))
return;
playingMidi[alias] = true;
}
public static bool isPlaying(String alias)
{
return playingMidi.ContainsKey(alias);
}
private static void StartAndStopMidiWithDelay(String fileName, String alias)
{
mciSendString("open " + fileName + " type sequencer alias " + alias, null, 0, new IntPtr());
mciSendString("play " + alias, null, 0, new IntPtr());
StringBuilder result = new StringBuilder(100);
mciSendString("set " + alias + " time format milliseconds", null, 0, new IntPtr());
mciSendString("status " + alias + " length", result, 100, new IntPtr());
int midiLengthInMilliseconds;
Int32.TryParse(result.ToString(), out midiLengthInMilliseconds);
Stopwatch timer = new Stopwatch();
timer.Start();
while(timer.ElapsedMilliseconds < midiLengthInMilliseconds && !playingMidi[alias])
{
}
timer.Stop();
StopMidi(alias);
}
private static void StopMidi(String alias)
{
if (!playingMidi.ContainsKey(alias))
throw new Exception("Midi with alias '" + alias + "' is already stopped");
// Execute calls to close and stop the player, on the same thread as the play and open calls
mciSendString("stop " + alias, null, 0, new IntPtr());
mciSendString("close " + alias, null, 0, new IntPtr());
playingMidi.Remove(alias);
}
A new player emerges:
https://github.com/atsushieno/managed-midi
https://www.nuget.org/packages/managed-midi/
Not much in the way of documentation, but one focus of this library is cross platform support.
System.Media.SoundPlayer is a good, simple way of playing WAV files. WAV files have some advantages over MIDI, one of them being that you can control precisely what each instrument sounds like (rather than relying on the computer's built-in synthesizer).

Categories

Resources