Ejemplo n.º 1
0
 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));
        }