[英]How to play multiple sounds at once in C#
我希望能夠一次播放多個聲音。 我使用多線程嘗試過這個,但發現它們仍然會一個接一個地播放。 有沒有辦法讓他們同時玩?
static void Main(string[] args)
{
Console.WriteLine("Hello World");
Thread th = new Thread(playSound);
Thread th1 = new Thread(playSound1);
th.Start();
th1.Start();
}
public static void playSound()
{
System.Media.SoundPlayer s1 = new System.Media.SoundPlayer(@"c:\Users\Ben\Documents\c#\note_c.wav");
s1.Load();
s1.PlaySync();
}
public static void playSound1()
{
System.Media.SoundPlayer s1 = new System.Media.SoundPlayer(@"c:\Users\Ben\Documents\c#\note_e.wav");
s1.Load();
s1.PlaySync();
}
}
如果我們安排並行執行怎么樣。 像那樣:
var files = new List<string>() {"note_1.wav", "note_2.wav"};
Parallel.ForEach(files, (currentFile) =>
{
System.Media.SoundPlayer s1 = new System.Media.SoundPlayer(currentFile);
s1.Load();
s1.PlaySync();
});
嘗試使用
[DllImport("winmm.dll")]
static extern Int32 mciSendString(string command, StringBuilder buffer, int bufferSize, IntPtr hwndCallback);
用法:
mciSendString(@"open path\to\your\file.wav type waveaudio alias anAliasForYourSound", null, 0, IntPtr.Zero);
mciSendString(@"play anAliasForYourSound", null, 0, IntPtr.Zero);
mciSendString(@"open path\to\your\anotherFile.wav type waveaudio alias anAliasForYourAnotherSound", null, 0, IntPtr.Zero);
mciSendString(@"play anAliasForYourAnotherSound", null, 0, IntPtr.Zero);
您不能完全同時啟動線程,但在這種情況下,您可以通過在啟動線程之前加載聲音來改進代碼:
static void Main(string[] args)
{
System.Media.SoundPlayer s1 = new System.Media.SoundPlayer(
@"c:\Users\Ben\Documents\c#\note_c.wav");
s1.Load();
System.Media.SoundPlayer s2 = new System.Media.SoundPlayer(
@"c:\Users\Ben\Documents\c#\note_e.wav");
s2.Load();
Console.WriteLine("Hello World");
Thread th = new Thread(() => playSound(s1));
Thread th1 = new Thread(() => playSound(s2));
th.Start();
th1.Start();
}
public static void playSound(System.Media.SoundPlayer s)
{
s.PlaySync();
}
否則,您將需要實施同步系統。
最好的方法是通過 SharpDX 使用 DirectX xAudio2,你可以在這里找到它: https : //github.com/sharpdx/SharpDX-Samples/tree/master/Desktop/XAudio2/PlaySound
它工作得很好,但是通過使用事件和任務而不是 while 循環和同步執行來使代碼更好,並且能夠動態加載 xAudio2 圖形以更改聲音而無需重建整個圖形會有點棘手.
添加這些 Nuget 包 SharpDx SharpDX.MediaFoundation SharpDx.Xaudio2 並使用下面的代碼,它使用 DirectX xAudio2 並使用混音器構建圖形,它也可以播放 WAV 8 和 16
public class SoundServices : IDisposable
{
/// <summary>
/// Gets current sound file
/// </summary>
string SoundFile { get; }
/// <summary>
/// Gets current sound stream
/// </summary>
public Stream SoundStream { get; private set; }
/// <summary>
/// Gets/Sets looping option, sound will loop if set to true.
/// </summary>
public bool IsLooping { get; private set; }
/// <summary>
/// Holds the message of last error if any, check it if some feature fails
/// </summary>
public string LastErrorMsg { get; private set; }
//Play control flags
bool IsPlaying = false;
bool IsUserStop = false;
AutoResetEvent Playing;
float CurrentVolume = 1.0f;
bool IsInitialized = false;
SoundStream stream;
WaveFormat waveFormat;
AudioBuffer buffer;
SourceVoice sourceVoice;
XAudio2 xaudio2;
MasteringVoice masteringVoice;
/// <summary>
/// Initializes an instance by creating xAudio2 graph and preparing audio Buffers
/// </summary>
/// <param name="soundStream">WAV format sound stream</param>
/// <param name="loop">Bool indicating to loop sound playing or not</param>
public SoundServices(Stream soundStream, bool loop)
{
try
{
if (soundStream == null)
{
throw new ArgumentNullException("soundStream", "Null is not allowed, please specify a valid stream");
}
SoundStream = soundStream;
SoundFile = null;
IsLooping = loop;
//Playing = new ManualResetEvent(false);
Playing = new AutoResetEvent(false);
BuildxAudio2Graph();
}
catch (Exception e)
{
throw e;
}
}
#region Sound Play API
/// <summary>
/// Plays the sound stream loaded during initialization
/// </summary>
/// <returns>Task of sound playing</returns>
public Task PlaySound()
{
return Task.Factory.StartNew(() =>
{
PlayRepeatAsync();
});
}
/// <summary>
/// Task for starting play of sound buffers using xAudio2 graph
/// </summary>
void PlayRepeatAsync()
{
try
{
IsPlaying = true;
if (buffer == null)
{
//stream = new SoundStream(SoundStream);
//waveFormat = stream.Format;
buffer = new AudioBuffer
{
Stream = stream.ToDataStream(),
AudioBytes = (int)stream.Length,
Flags = SharpDX.XAudio2.BufferFlags.EndOfStream
};
if (IsLooping)
buffer.LoopCount = AudioBuffer.LoopInfinite;
sourceVoice.SubmitSourceBuffer(buffer, stream.DecodedPacketsInfo);
//Close the stream as it is now loaded in buffer already
//stream.Close();
}
sourceVoice.Start();
Playing.WaitOne();
sourceVoice.Stop();
IsPlaying = false;
sourceVoice.FlushSourceBuffers();
//xAudio2 graph creation step (5) send the AudioBuffer to sourceVoice
sourceVoice.SubmitSourceBuffer(buffer, stream.DecodedPacketsInfo);
}
catch (Exception e)
{
LastErrorMsg = "PlayRepeatAsync(): "+ e.Message;
IsPlaying = false;
}
}
/// <summary>
/// Initializes xAudio2 graph to be used for sound playing
/// </summary>
void BuildxAudio2Graph()
{
try
{
stream = new SoundStream(SoundStream);
waveFormat = stream.Format;
//xAudio2 graph creation step (1) Create XAudio2 device
xaudio2 = new XAudio2();
//xAudio2 graph creation step (2) Create MasteringVoice and connect it to device
//*Note: You must use aMasteringVoice to connect to xAudioDevice
masteringVoice = new MasteringVoice(xaudio2);
//SetVolume(CurrentVolume);
//xAudio2 graph creation step (3) Prepare sourceVoice
sourceVoice = new SourceVoice(xaudio2, waveFormat, true);
// Adds a callback check buffer end and Looping option
sourceVoice.BufferEnd += SourceVoice_BufferEnd;
//xAudio2 graph creation step (5) send the AudioBuffer to sourceVoice
IsInitialized = true;
}
catch (Exception e)
{
LastErrorMsg = "BuildxAudio2Graph(): " + e.Message;
IsInitialized = false;
}
}
/// <summary>
/// Recreates source buffer allowing sound change or loop change
/// </summary>
void RecreateBuffer()
{
try
{
SoundStream.Seek(0, SeekOrigin.Begin);
stream = new SoundStream(SoundStream);
waveFormat = stream.Format;
//if (buffer == null)
{
//stream = new SoundStream(SoundStream);
//waveFormat = stream.Format;
buffer = new AudioBuffer
{
Stream = stream.ToDataStream(),
AudioBytes = (int)stream.Length,
Flags = SharpDX.XAudio2.BufferFlags.EndOfStream
};
if (IsLooping)
buffer.LoopCount = AudioBuffer.LoopInfinite;
sourceVoice.FlushSourceBuffers();
sourceVoice.SubmitSourceBuffer(buffer, stream.DecodedPacketsInfo);
//Close the stream as it is now loaded in buffer already
//stream.Close();
}
}
catch (Exception e)
{
LastErrorMsg = "RecreateBuffer(): " + e.Message;
}
}
/// <summary>
/// Changes current audio to a new one
/// </summary>
/// <param name="soundStream">a stream from wav file</param>
public void ChangeSoundTo(Stream soundStream, bool Loop)
{
try
{
IsLooping = Loop;
SoundStream = soundStream;
stream = new SoundStream(SoundStream);
waveFormat = stream.Format;
sourceVoice = new SourceVoice(xaudio2, waveFormat, true);
sourceVoice.BufferEnd += SourceVoice_BufferEnd;
RecreateBuffer();
}
catch (Exception e)
{
IsInitialized = false;
LastErrorMsg = "ChangeSoundTo(): " + e.Message;
}
}
/// <summary>
/// Set loop ot no loop
/// </summary>
/// <param name="loop">True = Loop forever, false = play till end</param>
public void SetLooping(bool loop)
{
if (IsPlaying)
Stop();
IsLooping = loop;
RecreateBuffer();
}
#endregion
/// <summary>
/// Immediately Stops currently playing sound
/// </summary>
public void Stop()
{
try
{
if (IsPlaying)
{
IsUserStop = true;
Playing.Set();
}
}
catch (Exception e)
{
LastErrorMsg = "Stop(): " + e.Message;
}
}
/// <summary>
/// Gets Current Volume
/// </summary>
/// <returns>Current volume</returns>
public float GetVolume()
{
float current = 0.0f;
try
{
if (sourceVoice == null || sourceVoice.IsDisposed) return CurrentVolume;
sourceVoice.GetVolume(out current);
}
catch (Exception e)
{
LastErrorMsg = "GetVolume(): " + e.Message;
}
return current;
}
/// <summary>
/// Sets the current volume
/// </summary>
/// <param name="newVolume">returns back the current setting for confirmation</param>
/// <returns>The current set volume</returns>
public float SetVolume(float newVolume)
{
try
{
if (newVolume > 1 || newVolume < 0) return GetVolume();
if (sourceVoice == null || sourceVoice.IsDisposed)
{
CurrentVolume = newVolume;
return newVolume;
}
sourceVoice.SetVolume(newVolume, 0);
return GetVolume();
}
catch (Exception e)
{
LastErrorMsg = "SetVolume(): " + e.Message;
return 0.0f;
}
}
/// <summary>
/// End of buffer event handler
/// </summary>
/// <param name="obj"></param>
private void SourceVoice_BufferEnd(IntPtr obj)
{
//Debug.WriteLine($"buffer end reached with looping {IsLooping}");
if (!IsLooping)
{
if (IsPlaying && !IsUserStop)
Playing.Set();
else if(IsUserStop)
{
IsUserStop = false;
}
}
}
public void Dispose()
{
if (sourceVoice != null && !sourceVoice.IsDisposed)
{
sourceVoice.DestroyVoice();
sourceVoice.Dispose();
}
if (buffer != null && buffer.Stream != null)
buffer.Stream.Dispose();
if (masteringVoice != null && !masteringVoice.IsDisposed)
masteringVoice.Dispose();
if (xaudio2 != null && !xaudio2.IsDisposed)
xaudio2.Dispose();
}
~SoundServices()
{
if (sourceVoice != null && !sourceVoice.IsDisposed)
{
sourceVoice.DestroyVoice();
sourceVoice.Dispose();
}
if (buffer != null && buffer.Stream != null)
buffer.Stream.Dispose();
if (masteringVoice != null && !masteringVoice.IsDisposed)
masteringVoice.Dispose();
if (xaudio2 != null && !xaudio2.IsDisposed)
xaudio2.Dispose();
}
}
您可以根據需要使用多個 SoundServices 實例:
var sp = new SoundServices(new MemoryStream(File.ReadAllBytes(WavFileName)), true);
if (sp != null)
sp.PlaySound();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.