예제 #1
0
        /// <summary>
        /// Determine if the device will support a given PCM format
        /// </summary>
        /// <param name="sampleFormat">The data format for PCM samples</param>
        /// <param name="channels">The number of channels</param>
        /// <param name="sampleRate">The number of frames/second the device will render/capture</param>
        /// <param name="suggestedLatency">The latency the device will attempt to match</param>
        /// <param name="asOutput">Marks if the device should be opened as an input or an output</param>
        /// <returns>True if the device supports this format, false otherwise</returns>
        public unsafe bool SupportsFormat(PortAudioSampleFormat sampleFormat, int channels, double sampleRate, TimeSpan suggestedLatency, bool asOutput)
        {
            PaStreamParameters *inputParams  = default;
            PaStreamParameters *outputParams = default;

            var param = new PaStreamParameters
            {
                DeviceIndex  = DeviceIndex,
                ChannelCount = channels,
                HostApiSpecificStreamInfo = IntPtr.Zero,
                SampleFormats             = sampleFormat.SampleFormat,
                SuggestedLatency          = new PaTime(suggestedLatency)
            };

            if (asOutput)
            {
                outputParams = &param;
            }
            else
            {
                inputParams = &param;
            }

            return(Native.PortAudio.Pa_IsFormatSupported(inputParams, outputParams, sampleRate) >= PaErrorCode.NoError);
        }
        /// <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));
        }