public static void UnRegister(object target)
        {
            lock (SyncObject)
            {
                if (!RegisteredHandles.Remove(RuntimeHelpers.GetHashCode(target)))
                {
                    return;
                }

                Debug.WriteLine($"Unregistered an object, now down to {RegisteredHandles.Count}");

                if (RegisteredHandles.Count != 0)
                {
                    return;
                }

                Debug.WriteLine("Terminating PortAudio...");

                PortAudioInstanceCache.ClearCache();

                var err = Native.PortAudio.Pa_Terminate();
                if (err < PaErrorCode.NoError)
                {
                    throw PortAudioException.GetException(err);
                }
            }
        }
        /// <summary>
        /// Stop playback without waiting for existing data to drain.
        /// </summary>
        public void Abort()
        {
            var err = Native.PortAudio.Pa_AbortStream(_stream);

            if (err < PaErrorCode.NoError)
            {
                throw PortAudioException.GetException(err);
            }
        }
예제 #3
0
        public static PortAudioHostApi GetHostApi(PaHostApiIndex index)
        {
            if (index.TryGetErrorCode(out var err))
            {
                throw PortAudioException.GetException(err);
            }

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

            var api = new PortAudioHostApi(index);

            ApiCache[index] = new WeakReference <PortAudioHostApi>(api);
            return(api);
        }
예제 #4
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);
            }
        }
        public static void Register(object target)
        {
            lock (SyncObject)
            {
                if (RegisteredHandles.Count == 0)
                {
                    Debug.WriteLine("Initializing PortAudio...");

                    var err = Native.PortAudio.Pa_Initialize();
                    if (err < PaErrorCode.NoError)
                    {
                        throw PortAudioException.GetException(err);
                    }
                }

                RegisteredHandles.Add(RuntimeHelpers.GetHashCode(target));

                Debug.WriteLine($"Registered a new object, now up to {RegisteredHandles.Count}");
            }
        }
        /// <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));
        }