PortAudioでマイク入力をそのままスピーカーに出力するだけのコード
portaudiosharp - .NET bindings for the portable low-latency audio library PortAudio - Google Project Hostingを使えばいいのに、ちょっとラッパーの実装が気に入らないので自分でアンマネージドコードを呼び出してみている。(ちなみにportaudiosharpのPortAudioクラスにP/Invokeは全部定義されている)
メイン
class Program { private const double SampleRate = 44100.0; private const int FramesPerBuffer = 2048; private static float[] buffer = new float[FramesPerBuffer]; static void Main(string[] args) { Pa_Initialize(); var inputDevice = 2;//Pa_GetDefaultInputDevice(); var inputDeviceInfo = (PaDeviceInfo)Marshal.PtrToStructure( Pa_GetDeviceInfo(inputDevice), typeof(PaDeviceInfo)); var outputDevice = Pa_GetDefaultOutputDevice(); var outputDeviceInfo = (PaDeviceInfo)Marshal.PtrToStructure( Pa_GetDeviceInfo(outputDevice), typeof(PaDeviceInfo)); var inputParams = new PaStreamParameters() { channelCount = 1, device = inputDevice, sampleFormat = PaSampleFormat.Float32, suggestedLatency = inputDeviceInfo.defaultLowInputLatency, }; var outputParams = new PaStreamParameters() { channelCount = 1, device = outputDevice, sampleFormat = PaSampleFormat.Float32, suggestedLatency = outputDeviceInfo.defaultLowInputLatency, }; var stream = IntPtr.Zero; Pa_OpenStream( out stream, ref inputParams, ref outputParams, SampleRate, FramesPerBuffer, PaStreamFlags.NoFlag, HandleCallback, IntPtr.Zero); Pa_StartStream(stream); System.Threading.Thread.Sleep(10000); //10秒間 Pa_StopStream(stream); Pa_Terminate(); } private static PaStreamCallbackResult HandleCallback( IntPtr input, IntPtr output, uint frameCount, ref PaStreamCallbackTimeInfo timeInfo, PaStreamCallbackFlags statusFlags, IntPtr userData) { try { Marshal.Copy(input, buffer, 0, FramesPerBuffer); Marshal.Copy(buffer, 0, output, FramesPerBuffer); } catch (Exception e) { Console.WriteLine(e.ToString()); } return PaStreamCallbackResult.Continue; } }
P/Invokeシグネチャ
[DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)] private extern static PaError Pa_Initialize(); [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)] private extern static PaError Pa_Terminate(); [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)] private extern static int Pa_GetDefaultInputDevice(); [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)] private extern static int Pa_GetDefaultOutputDevice(); [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)] private extern static IntPtr Pa_GetDeviceInfo(int index); [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)] private static extern PaError Pa_OpenStream( out IntPtr stream, ref PaStreamParameters inputParameters, ref PaStreamParameters outputParameters, double sampleRate, uint framesPerBuffer, PaStreamFlags streamFlags, PaStreamCallbackDelegate streamCallback, IntPtr userData); [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)] private extern static PaError Pa_StartStream(IntPtr stream); [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)] private extern static PaError Pa_StopStream(IntPtr stream); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate PaStreamCallbackResult PaStreamCallbackDelegate( IntPtr input, IntPtr output, uint frameCount, ref PaStreamCallbackTimeInfo timeInfo, PaStreamCallbackFlags statusFlags, IntPtr userData); enum PaError : int { NoError = 0, NotInitialized = -10000, UnanticipatedHostError, InvalidChannelCount, InvalidSampleRate, InvalidDevice, InvalidFlag, SampleFormatNotSupported, BadIODeviceCombination, InsufficientMemory, BufferTooBig, BufferTooSmall, NullCallback, BadStreamPtr, TimedOut, InternalError, DeviceUnavailable, IncompatibleHostApiSpecificStreamInfo, StreamIsStopped, StreamIsNotStopped, InputOverflowed, OutputUnderflowed, HostApiNotFound, InvalidHostApi, CanNotReadFromACallbackStream, CanNotWriteToACallbackStream, CanNotReadFromAnOutputOnlyStream, CanNotWriteToAnInputOnlyStream, IncompatibleStreamHostApi, BadBufferPtr } [StructLayout(LayoutKind.Sequential)] struct PaDeviceInfo { public int structVersion; [MarshalAs(UnmanagedType.LPStr)] public string name; public int hostApi; public int maxInputChannels; public int maxOutputChannels; public double defaultLowInputLatency; public double defaultLowOutputLatency; public double defaultHighInputLatency; public double defaultHighOutputLatency; public double defaultSampleRate; } [StructLayout(LayoutKind.Sequential)] public struct PaStreamParameters { public int device; public int channelCount; public PaSampleFormat sampleFormat; public double suggestedLatency; public IntPtr hostApiSpecificStreamInfo; } [StructLayout(LayoutKind.Sequential)] public struct PaStreamCallbackTimeInfo { public double inputBufferAdcTime; public double currentTime; public double outputBufferDacTime; } public enum PaSampleFormat : uint { Float32 = 0x00000001, Int32 = 0x00000002, Int24 = 0x00000004, Int16 = 0x00000008, Int8 = 0x00000010, UInt8 = 0x00000020, CustomFormat = 0x00010000, NonInterleaved = 0x80000000, } public enum PaStreamFlags : uint { NoFlag = 0, ClipOff = 0x00000001, DitherOff = 0x00000002, NeverDropInput = 0x00000004, PrimeOutputBuffersUsingStreamCallback = 0x00000008, PlatformSpecificFlags = 0xFFFF0000 } public enum PaStreamCallbackResult : uint { Continue = 0, Complete = 1, Abort = 2 } public enum PaStreamCallbackFlags : uint { InputUnderflow = 0x00000001, InputOverflow = 0x00000002, OutputUnderflow = 0x00000004, OutputOverflow = 0x00000008, PrimingOutput = 0x00000010 }