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
}