数年前、私は自分の会社用に、特定の 1 つのモデルのコンピューターのみを実行するカスタム アプリケーションを作成しました。アプリケーションは、マイク ジャックから入ってくるオーディオをスピーカーにパススルーできる必要がありました。ジャックに入るバイトを処理してソフトウェアでスピーカーに渡す代わりに、特定のハードウェアを知っているという事実を利用して、サウンドカードの組み込み機能を有効にして、入力からスピーカーにオーディオをループさせる関数を記述しました。 . これがその関数です (mmsystem.dll のみを使用して C で記述されています)。
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;
}
ご覧のとおり、このメソッドは、ミキサーの特定のプロパティにアクセスするために多くの配列インデックスをハードコーディングしたため、当時使用していたハードウェアに非常に固有のものです。
それでは質問です。
数年が経ち、現在 C# winforms で作成しているアプリケーションを変更して、同じ動作を示す必要があります。つまり、マイクまたはリニ入力ジャックから受信したオーディオを直接スピーカーにパススルーする必要があります。ここでのトリックは、ハードウェアが閉じられていないことです。また、アプリケーションは、WinXP 以降を実行している任意のマシンで実行する必要があります。
このパススルーをソフトウェア モードで実行するために、NAudio ライブラリを使用し始めました (組み込みのサウンド カード パススルーは使用しません)。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;
}
}
}
}
これで私が抱えている問題は(私が推測する)リソースです。このコードを使用すると、オーディオをスピーカーにループ出力できますが、システムでタスクを実行すると、オーディオにポップやスキップが発生します。たとえば、アプリケーションを開くか、フォルダーをすばやく最小化および最大化すると、再生がポップしたりスキップしたりします。
このポッピングとスキップを回避するために、何らかの方法で NAudio ライブラリをスレッド化する方法はありますか? それとも、何年も前に C アプリケーションで行ったように、ハードウェアを介してオーディオをパススルーする一般的な方法を見つけたほうがよいのでしょうか?
編集:
このオーディオ ツールボックスをテストするアプリケーションは非常に単純です。これは、Visual Studio 2010 によって作成された単なる既定の winforms アプリケーションです。フォームに 1 つのボタンを追加し、クリック イベントで次のイベントが発生します。
private void button1_Click(object sender, EventArgs e)
{
AudioToolbox atr = new AudioToolbox();
atr.BeginReading(0);
atr.BeginLoopback();
}
また、プロジェクトを .NET Framework 4 で実行するように設定しました。これは、最終的にこのツールボックスを統合する必要があるアプリケーションのフレームワークだからです。アプリケーションをコンパイルしてボタンをクリックすると、オーディオがマイク ジャックからスピーカーに渡されるのが聞こえます。次に、Windows ファイル エクスプローラーを開き、継続的に最小化/最大化します。このアクションにより、オーディオがスキップされます。失敗。
この質問を NAudio フォーラムにも投稿しました。将来誰かがこのページに出くわした場合に備えて、そのリンクは次の とおりです。NAudioフォーラムに投稿された質問