如何在 C# 控制台应用程序中正确实现声音?
How to implement sound in a C# Console Application correctly?
我创建了一个控制台应用程序,可以在控制台屏幕的任意位置创建文本。我想创建一个类似打字机的效果,所以我从打字机导入了击键声音并在我的项目中使用它。当在屏幕上输入字符时很难同步播放声音,所以我创建了一个名为 Sounds 的 class,它为我想要 运行 的每个声音创建了一个后台线程在后台。
现在我的角色已与打字机的声音同步,我添加了一个新的声音文件。只要有新行,该文件就应该播放。我现在面临的问题是,新的打字机托架 return 声音正在播放并突然停止。为了解决这个问题,我在 SoundPlayer 实例上添加了 PlaySync() 命令。这允许我在后台播放新文件,但是当执行下一条消息时,在向控制台键入字符时,回车 return 声音仍在播放。回车return结束后,击键声恢复正常。
我明白了发生这种情况的原因:PlaySync() 将确保加载和播放声音,然后恢复正常操作。如果我使用 PlaySync 以外的任何东西,return 的速度快到连声音都听不见。我试图添加延迟,但它仍然不完美。我希望能够在键入字符后立即听到击键的声音。当执行新行时,我希望能够听到马车 return 的声音。所有进程必须等待此马车 return 声音完成其循环后。同步这些声音的正确方法是什么?我的逻辑有问题吗?
Screen.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CanizHospital
{
public class Screen
{
private Sounds sounds;
private const int delay = 300;
private static int _leftPos;
private static int _topPos;
public Screen(int leftPos, int topPos, int screenWidth, int screenHeight)
{
_leftPos = leftPos;
_topPos = topPos;
sounds = new Sounds();
SetUpScreen(screenWidth, screenHeight);
}
private static void SetUpScreen(int width, int height)
{
IntPtr ptr = GetConsoleWindow();
MoveWindow(ptr, 0, 0, 1000, 400, true);
Console.SetWindowSize(width, height);
}
public void WriteAt(string message, int x, int y, bool typeWritter)
{
try
{
Console.SetCursorPosition(_leftPos + x, _topPos + y);
if(typeWritter && message != null)
{
TypeWritter(message, delay);
}
}
catch(ArgumentOutOfRangeException e)
{
Console.Clear();
Console.Beep(37, 500);
Console.Write(e.Message);
}
}
public void TypeWritter(string message, int delay, bool newLine = true)
{
foreach (char c in message)
{
Console.Write(c);
sounds.LoadTypewriterSound();
Thread.Sleep(delay);
}
if(newLine)
{
Console.Write(Environment.NewLine);
sounds.LoadCarriageReturn();
Thread.Sleep(delay);
}
}
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
}
}
Sounds.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Media;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CanizHospital
{
class Sounds
{
public void LoadTypewriterSound()
{
Thread backgroundSound = new Thread(new ThreadStart(PlayKey));
backgroundSound.IsBackground = true;
backgroundSound.Start();
}
public void LoadCarriageReturn()
{
Thread backgroundSound = new Thread(new ThreadStart(PlayCarriageReturn));
backgroundSound.IsBackground = true;
backgroundSound.Start();
}
private static void PlayKey()
{
SoundPlayer player = new SoundPlayer();
player.SoundLocation = @"C:\Users\Erick\Desktop\C#\CanizHospital\CanizHospital\typewriter-key-1.wav";
player.Play();
}
private static void PlayCarriageReturn()
{
SoundPlayer player = new SoundPlayer();
player.SoundLocation = @"C:\Users\Erick\Desktop\C#\CanizHospital\CanizHospital\typewriter-return-1.wav";
player.PlaySync();
}
}
}
主要
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Threading;
using Console = Colorful.Console;
using Colorful;
namespace CanizHospital
{
class Program
{
static void Main(string[] args)
{
Screen screen = new Screen(Console.CursorLeft, Console.CursorTop,
Console.LargestWindowWidth, Console.LargestWindowHeight);
screen.WriteAt("Hi whats up", 0, 0, true);
//Thread.sleep(500); //Delay here wont stop process
screen.WriteAt("Hi whats up", 1, 1, true);
}
}
}
首先,你不需要创建一个新线程来持有一个SoundPlayer
实例来调用Play()
。您可以在 Console.Write
之前调用 Play()
并在延迟一段时间后调用 Stop()
(或者您听不到任何声音,因为它停止得太快)。来自 MSDN,Play()
方法
Plays the .wav file using a new thread, and loads the .wav file first if it has not been loaded.
其次,PlaySync()
在执行结束前阻塞执行,正好满足你的要求:
The PlaySync method uses the current thread to play a .wav file, preventing the thread from handling other messages until the load is complete.
下面是按您要求的方式工作的代码片段:
public void TypeWritter(string message, int delay, bool newLine = true)
{
var player = new SoundPlayer
{
SoundLocation = @"C:\Users\Erick\Desktop\C#\CanizHospital\CanizHospital\typewriter-key-1.wav"
};
foreach (char c in message)
{
player.Play();
Console.Write(c);
Thread.Sleep(delay);
player.Stop();
}
if (newLine)
{
Console.Write(Environment.NewLine);
player.SoundLocation = @"C:\Users\Erick\Desktop\C#\CanizHospital\CanizHospital\typewriter-return-1.wav";
player.PlaySync();
//Thread.Sleep(delay); // Might not be necessary
}
}
我创建了一个控制台应用程序,可以在控制台屏幕的任意位置创建文本。我想创建一个类似打字机的效果,所以我从打字机导入了击键声音并在我的项目中使用它。当在屏幕上输入字符时很难同步播放声音,所以我创建了一个名为 Sounds 的 class,它为我想要 运行 的每个声音创建了一个后台线程在后台。
现在我的角色已与打字机的声音同步,我添加了一个新的声音文件。只要有新行,该文件就应该播放。我现在面临的问题是,新的打字机托架 return 声音正在播放并突然停止。为了解决这个问题,我在 SoundPlayer 实例上添加了 PlaySync() 命令。这允许我在后台播放新文件,但是当执行下一条消息时,在向控制台键入字符时,回车 return 声音仍在播放。回车return结束后,击键声恢复正常。
我明白了发生这种情况的原因:PlaySync() 将确保加载和播放声音,然后恢复正常操作。如果我使用 PlaySync 以外的任何东西,return 的速度快到连声音都听不见。我试图添加延迟,但它仍然不完美。我希望能够在键入字符后立即听到击键的声音。当执行新行时,我希望能够听到马车 return 的声音。所有进程必须等待此马车 return 声音完成其循环后。同步这些声音的正确方法是什么?我的逻辑有问题吗?
Screen.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CanizHospital
{
public class Screen
{
private Sounds sounds;
private const int delay = 300;
private static int _leftPos;
private static int _topPos;
public Screen(int leftPos, int topPos, int screenWidth, int screenHeight)
{
_leftPos = leftPos;
_topPos = topPos;
sounds = new Sounds();
SetUpScreen(screenWidth, screenHeight);
}
private static void SetUpScreen(int width, int height)
{
IntPtr ptr = GetConsoleWindow();
MoveWindow(ptr, 0, 0, 1000, 400, true);
Console.SetWindowSize(width, height);
}
public void WriteAt(string message, int x, int y, bool typeWritter)
{
try
{
Console.SetCursorPosition(_leftPos + x, _topPos + y);
if(typeWritter && message != null)
{
TypeWritter(message, delay);
}
}
catch(ArgumentOutOfRangeException e)
{
Console.Clear();
Console.Beep(37, 500);
Console.Write(e.Message);
}
}
public void TypeWritter(string message, int delay, bool newLine = true)
{
foreach (char c in message)
{
Console.Write(c);
sounds.LoadTypewriterSound();
Thread.Sleep(delay);
}
if(newLine)
{
Console.Write(Environment.NewLine);
sounds.LoadCarriageReturn();
Thread.Sleep(delay);
}
}
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
}
}
Sounds.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Media;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CanizHospital
{
class Sounds
{
public void LoadTypewriterSound()
{
Thread backgroundSound = new Thread(new ThreadStart(PlayKey));
backgroundSound.IsBackground = true;
backgroundSound.Start();
}
public void LoadCarriageReturn()
{
Thread backgroundSound = new Thread(new ThreadStart(PlayCarriageReturn));
backgroundSound.IsBackground = true;
backgroundSound.Start();
}
private static void PlayKey()
{
SoundPlayer player = new SoundPlayer();
player.SoundLocation = @"C:\Users\Erick\Desktop\C#\CanizHospital\CanizHospital\typewriter-key-1.wav";
player.Play();
}
private static void PlayCarriageReturn()
{
SoundPlayer player = new SoundPlayer();
player.SoundLocation = @"C:\Users\Erick\Desktop\C#\CanizHospital\CanizHospital\typewriter-return-1.wav";
player.PlaySync();
}
}
}
主要
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Threading;
using Console = Colorful.Console;
using Colorful;
namespace CanizHospital
{
class Program
{
static void Main(string[] args)
{
Screen screen = new Screen(Console.CursorLeft, Console.CursorTop,
Console.LargestWindowWidth, Console.LargestWindowHeight);
screen.WriteAt("Hi whats up", 0, 0, true);
//Thread.sleep(500); //Delay here wont stop process
screen.WriteAt("Hi whats up", 1, 1, true);
}
}
}
首先,你不需要创建一个新线程来持有一个SoundPlayer
实例来调用Play()
。您可以在 Console.Write
之前调用 Play()
并在延迟一段时间后调用 Stop()
(或者您听不到任何声音,因为它停止得太快)。来自 MSDN,Play()
方法
Plays the .wav file using a new thread, and loads the .wav file first if it has not been loaded.
其次,PlaySync()
在执行结束前阻塞执行,正好满足你的要求:
The PlaySync method uses the current thread to play a .wav file, preventing the thread from handling other messages until the load is complete.
下面是按您要求的方式工作的代码片段:
public void TypeWritter(string message, int delay, bool newLine = true)
{
var player = new SoundPlayer
{
SoundLocation = @"C:\Users\Erick\Desktop\C#\CanizHospital\CanizHospital\typewriter-key-1.wav"
};
foreach (char c in message)
{
player.Play();
Console.Write(c);
Thread.Sleep(delay);
player.Stop();
}
if (newLine)
{
Console.Write(Environment.NewLine);
player.SoundLocation = @"C:\Users\Erick\Desktop\C#\CanizHospital\CanizHospital\typewriter-return-1.wav";
player.PlaySync();
//Thread.Sleep(delay); // Might not be necessary
}
}