Mic*_*kus 7 c# hardware audio winforms
几年前,我为我的公司编写了一个自定义应用程序,该应用程序只运行一台特定型号的计算机.应用程序必须能够将麦克风插孔中的音频传递到扬声器.我没有处理进入插孔并通过软件传递给扬声器的字节,而是说明了我知道特定的硬件来编写一个功能,该功能使声卡的内置功能能够将音频从输入循环到扬声器.这是该函数(它是用C语言编写的,仅使用mmsystem.dll):
int setMasterLevelsFromMicrophone (int volume, int mute)
{
MMRESULT error;
// Open the mixer
HMIXER mixerHandle;
if (error = mixerOpen (&mixerHandle, 0, 0, 0, 0))
return 1;
// Get the microphone source information
MIXERLINE mixerline;
mixerline.cbStruct = sizeof(MIXERLINE);
mixerline.dwDestination = 0;
if ((error = mixerGetLineInfo((HMIXEROBJ)mixerHandle, &mixerline, MIXER_GETLINEINFOF_DESTINATION)))
return 2;
// Get the microhone source controls
MIXERCONTROL mixerControlArray[2];
MIXERLINECONTROLS mixerLineControls;
mixerLineControls.cbStruct = sizeof(MIXERLINECONTROLS);
mixerLineControls.cControls = 2;
mixerLineControls.dwLineID = mixerline.dwLineID;
mixerLineControls.pamxctrl = &mixerControlArray[0];
mixerLineControls.cbmxctrl = sizeof(MIXERCONTROL);
if ((error = mixerGetLineControls((HMIXEROBJ)mixerHandle, &mixerLineControls, MIXER_GETLINECONTROLSF_ALL)))
return 3;
// Set the microphone source volume
MIXERCONTROLDETAILS_UNSIGNED value;
MIXERCONTROLDETAILS mixerControlDetails;
mixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
mixerControlDetails.dwControlID = mixerControlArray[0].dwControlID;
mixerControlDetails.cChannels = 1;
mixerControlDetails.cMultipleItems = 0;
mixerControlDetails.paDetails = &value;
mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
value.dwValue = volume;
if ((error = mixerSetControlDetails((HMIXEROBJ)mixerHandle, &mixerControlDetails, MIXER_SETCONTROLDETAILSF_VALUE)))
return 4;
// Set the microphone source mute
mixerControlDetails.dwControlID = mixerControlArray[1].dwControlID;
value.dwValue = mute;
if ((error = mixerSetControlDetails((HMIXEROBJ)mixerHandle, &mixerControlDetails, MIXER_SETCONTROLDETAILSF_VALUE)))
return 5;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,此方法非常特定于我当时使用的硬件,因为我已经硬编码了许多数组索引来访问混音器的特定属性.
现在提出问题.
现在已经有几年了,我需要修改我目前在C#winforms中编写的应用程序,以显示相同的行为.也就是说,我需要从麦克风或lini-in插孔接收的音频直接传递到扬声器.这里的诀窍是硬件不再关闭.并且应用程序需要在运行WinXP或更高版本的任何计算机上运行.
我开始与n音讯库工作在软件模式做这个直通(不利用内置声卡直通).这是我在C#中创建的小工具箱:
using System;
using System.ComponentModel;
using NAudio.Wave;
namespace Media
{
public partial class AudioToolbox : Component
{
private WaveIn waveIn = null;
private WaveOutEvent waveOut = null;
public int SampleRate { get; set; }
public int BitsPerSample { get; set; }
public int Channels { get; set; }
public AudioToolbox()
{
InitializeComponent();
SampleRate = 22050;
BitsPerSample = 16;
Channels = 1;
}
public void BeginReading(int deviceNumber)
{
if (waveIn == null)
{
waveIn = new WaveIn();
waveIn.DeviceNumber = deviceNumber;
waveIn.WaveFormat = new NAudio.Wave.WaveFormat(SampleRate, BitsPerSample, Channels);
waveIn.StartRecording();
}
}
public void BeginLoopback()
{
if (waveIn != null && waveOut == null)
{
WaveInProvider waveInProvider = new WaveInProvider(waveIn);
waveOut = new WaveOutEvent();
waveOut.DeviceNumber = -1; // Default output device
waveOut.DesiredLatency = 300;
waveOut.Init(waveInProvider);
waveOut.Play();
}
}
public void EndReading()
{
if (waveIn != null)
{
waveIn.StopRecording();
waveIn.Dispose();
waveIn = null;
}
}
public void EndLoopback()
{
if (waveOut != null)
{
waveOut.Stop();
waveOut.Dispose();
waveOut = null;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我遇到的问题是(我假设)资源.此代码允许我将音频循环到扬声器,但在系统上执行任务会在音频中引入弹出和跳过.例如,如果我打开应用程序或快速最小化并最大化文件夹,则播放会弹出并跳过.
有没有办法以某种方式线程NAudio库,以避免这种弹出和跳过?或者,我是否更好地找到通过硬件传递音频的通用方法,就像我几年前用我的C应用程序做的那样?
编辑:
我测试此音频工具箱的应用程序非常简单.它仅仅是由Visual Studio 2010中创建一个默认的WinForms应用程序我添加一个按钮的形式和以下事件存在的上单击事件:
private void button1_Click(object sender, EventArgs e)
{
AudioToolbox atr = new AudioToolbox();
atr.BeginReading(0);
atr.BeginLoopback();
}
Run Code Online (Sandbox Code Playgroud)
我还将项目设置为在.NET Framework 4中运行,因为这是我需要此工具箱最终集成的应用程序框架.当我编译应用程序并单击按钮时,我可以听到音频从麦克风插孔传递到扬声器.然后我打开Windows文件浏览器并不断最小化/最大化它.此操作会导致音频跳过.失败.
我刚刚在NAudio论坛上发布了这个问题.如果将来有人偶然发现此页面,请点击此链接: 在NAudio论坛上发布的问题
这是迄今为止我能够实现的最大程度地减少跳过。我将接受它作为答案,以便任何其他偶然发现此页面的人都会看到我所做的事情,但如果有人提出更好的解决方案,我将很乐意选择他们的答案。
我要做的第一件事就是放弃 NAudio 1.5,这是最后一个官方 NAudio 版本。相反,我获取了最新的热门版本,它是 NAudio 1.6 的测试版。我这样做是因为 1.6 的测试版包含一个名为 WaveInEvent 的新 WaveInProvider。WaveInEvent 是有益的,因为它可以防止在从麦克风插孔读取数据时调用 GUI 线程。
我做的第二件事是从 WaveOutEvent 切换到 DirectSoundOut。我这样做是因为在测试中,我发现当从文件播放音频时,WaveOutEvent 会根据我对 CPU 的使用情况而跳过,但 DirectSoundOut 不会。因此,我假设从麦克风端口播放音频时会发生相同的行为。因此,我使用 DirectSoundOut 来播放麦克风中的音频。
这是我的新 AudioInputToolbox:
using System;
using System.ComponentModel;
using NAudio.Wave;
namespace Media
{
public partial class AudioInputToolbox : Component
{
private WaveInEvent waveIn = null;
private DirectSoundOut waveOut = null;
public int SampleRate { get; set; }
public int BitsPerSample { get; set; }
public int Channels { get; set; }
public AudioInputToolbox()
{
InitializeComponent();
SampleRate = 22050;
BitsPerSample = 16;
Channels = 1;
}
public void BeginReading(int deviceNumber)
{
if (waveIn == null)
{
waveIn = new WaveInEvent();
waveIn.DeviceNumber = deviceNumber;
waveIn.WaveFormat = new NAudio.Wave.WaveFormat(SampleRate, BitsPerSample, Channels);
waveIn.StartRecording();
}
}
public void BeginLoopback()
{
if (waveIn != null && waveOut == null)
{
waveOut = new DirectSoundOut(DirectSoundOut.DSDEVID_DefaultPlayback, 300);
waveOut.Init(new WaveInProvider(waveIn));
waveOut.Play();
}
}
public void EndReading()
{
if (waveIn != null)
{
waveIn.StopRecording();
waveIn.Dispose();
waveIn = null;
}
}
public void EndLoopback()
{
if (waveOut != null)
{
waveOut.Stop();
waveOut.Dispose();
waveOut = null;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的新测试应用程序的代码。它只是一个带有两个按钮的表单。每个按钮都有一个回调。一个是启动按钮。另一个是停止按钮。
using System;
using System.Threading;
using System.Windows.Forms;
using Media;
public partial class AITL : Form
{
AudioInputToolbox atr = new AudioInputToolbox();
public AITL()
{
InitializeComponent();
}
private void startButton_Click(object sender, EventArgs e)
{
new Thread(() =>
{
atr.BeginReading(0);
atr.BeginLoopback();
}).Start();
}
private void stopButton_Click(object sender, EventArgs e)
{
atr.EndReading();
atr.EndLoopback();
}
}
Run Code Online (Sandbox Code Playgroud)
这种方法并不能解决我的问题。它只会稍微减少问题发生的频率和稍微减轻问题的严重性。
再次,我很乐意接受任何能够完全解决跳过问题的人的不同答案。重申一下,在按下开始按钮并反复最小化和最大化窗口后,我遇到了跳过。任何窗口。我一直在windows资源管理器上这样做。(在我的全功能应用程序中,这个音频组件需要适应,有大量的 GUI 密集映射正在进行,因此窗口的最小化/最大化是对该操作的良好模拟)。