public static extern void SetReadDataCallback([In, MarshalAs(UnmanagedType.FunctionPtr)] ReadDataCallback readDataCallback);
/// <summary> /// Create a PortAudioDevicePump for an output device /// Note: This MUST be disposed, or you risk breaking audio on the host until the next reboot /// </summary> /// <param name="device">The output device to create a pump for</param> /// <param name="channelCount">The number of channels in the input PCM data</param> /// <param name="sampleFormat">The sample format of the input PCM data</param> /// <param name="suggestedLatency">The latency the device will attempt to achieve</param> /// <param name="sampleRate">The sample rate of playback</param> /// <param name="callback">The callback that will supply data to the output device</param> /// <exception cref="ArgumentNullException">Thrown in the case that the device or callback are null</exception> public unsafe PortAudioDevicePump(PortAudioDevice device, int channelCount, PortAudioSampleFormat sampleFormat, TimeSpan suggestedLatency, double sampleRate, ReadDataCallback callback) { if (device == null) { throw new ArgumentNullException(nameof(device)); } if (callback == null) { throw new ArgumentNullException(nameof(callback)); } PortAudioLifetimeRegistry.Register(this); SampleFormat = sampleFormat; Channels = channelCount; SampleRate = sampleRate; SuggestedLatency = suggestedLatency; _handle = GCHandle.Alloc(this); _isOutput = true; _readDataCallback = callback; var outputParams = new PaStreamParameters { ChannelCount = channelCount, DeviceIndex = device.DeviceIndex, HostApiSpecificStreamInfo = IntPtr.Zero, SampleFormats = sampleFormat.SampleFormat, SuggestedLatency = new PaTime(suggestedLatency) }; var err = Native.PortAudio.Pa_OpenStream(out _stream, null, &outputParams, sampleRate, FRAMES_TO_BUFFER, PaStreamFlags.NoFlag, StreamCallback, GCHandle.ToIntPtr(_handle)); if (err < PaErrorCode.NoError) { throw PortAudioException.GetException(err); } err = Native.PortAudio.Pa_SetStreamFinishedCallback(_stream, StreamFinishedCallback); if (err < PaErrorCode.NoError) { throw PortAudioException.GetException(err); } _dataQueue = new ConcurrentQueue <BufferContainer>(); _bufferPool = new ConcurrentBag <BufferContainer>(); for (var i = 0; i < BUFFER_CHAIN_LENGTH; i++) { var buffer = new byte[FRAMES_TO_BUFFER * (ulong)channelCount * (ulong)sampleFormat.FormatSize]; var readLength = WriteAudioFrame(buffer, buffer.Length); _dataQueue.Enqueue(new BufferContainer(buffer) { ReadLength = readLength }); } _queueCount = new SemaphoreSlim(BUFFER_CHAIN_LENGTH); _poolCount = new SemaphoreSlim(0); _threadEndEvent = new ManualResetEventSlim(false); _processingThreadCancel = new CancellationTokenSource(); Task.Run(() => DataTask(_processingThreadCancel.Token)); }