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); } }
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); }
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)); }