public InputStream(Device deviceToOwn) { _self = GCHandle.Alloc(this); _device = deviceToOwn; _stream = InStream.Create(_device); try { if (_stream.IsInvalid) { throw new InvalidOp("Stream allocation error"); } if (_device.Layouts.Length == 0) { throw new InvalidOp("No channel layout"); } // Calculate the best latency. // TODO: Should we use the target frame rate instead of 1/60? var bestLatency = Math.Max(1.0 / 60, _device.SoftwareLatencyMin); // Stream properties _stream.Format = Format.Float32LE; _stream.Layout = _device.Layouts[0]; _stream.SoftwareLatency = bestLatency; _stream.ReadCallback = _readCallback; _stream.OverflowCallback = _overflowCallback; _stream.ErrorCallback = _errorCallback; _stream.UserData = GCHandle.ToIntPtr(_self); var err = _stream.Open(); if (err != Error.None) { throw new InvalidOp($"Stream initialization error ({err})"); } // We want the buffers to meet the following requirements: // - Doesn't overflow if the main thread pauses for 4 frames. // - Doesn't overflow if the callback is invoked 4 times a frame. var latency = Math.Max(_stream.SoftwareLatency, bestLatency); var bufferSize = CalculateBufferSize((float)(latency * 4)); // Ring/window buffer allocation _ring = new RingBuffer(bufferSize); _window = new byte[bufferSize]; // Start streaming. _stream.Start(); } catch { // Dispose the resources on an exception. _stream.Dispose(); _device.Dispose(); _stream = null; _device = null; throw; } }