Exemplo n.º 1
0
        public static PortAudioDevice GetPortAudioDevice(PaDeviceIndex index)
        {
            if (index.TryGetErrorCode(out var err))
            {
                throw PortAudioException.GetException(err);
            }

            if (DeviceCache.TryGetValue(index, out var reference) && reference.TryGetTarget(out var target))
            {
                return(target);
            }

            var device = new PortAudioDevice(index);

            DeviceCache[index] = new WeakReference <PortAudioDevice>(device);
            return(device);
        }
        /// <summary>
        /// Create a PortAudioDevicePump for an input device
        /// Note: This MUST be disposed, or you risk breaking audio on the host until the next reboot
        /// </summary>
        /// <param name="device">The input device to create a pump for</param>
        /// <param name="channelCount">The number of channels to capture from the input device</param>
        /// <param name="sampleFormat">The sample format of the output PCM data</param>
        /// <param name="suggestedLatency">The latency the device will attempt to achieve</param>
        /// <param name="sampleRate">The sample rate of the output PCM data</param>
        /// <param name="callback">The callback that will be invoked when data is produced</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, WriteDataCallback 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          = false;
            _writeDataCallback = callback;

            var inputParams = 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, &inputParams, null, 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);
            }
        }
        /// <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));
        }