// IDisposable implementation public void Dispose() { _stream?.Dispose(); _stream = null; _device?.Dispose(); _device = null; if (_self.IsAllocated) { _self.Free(); } }
// Private constructor InputDeviceHandle(SoundIO.Device device) { _self = GCHandle.Alloc(this); _device = device; }
readonly float _bufferingTimeSec = 0.5f; // 0.5[sec] #endregion #region Stream initialization void OpenStream() { if (_device is null) { throw new InvalidOp("Invalid device"); } try { _stream = SoundIO.InStream.Create(_device); 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 = SoundIO.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 != SoundIO.Error.None) { throw new InvalidOp($"Stream initialization error ({err})"); } // Calculate buffer size var ringBufferSize = (int)(_stream.Layout.ChannelCount * _stream.SampleRate * _bufferingTimeSec); var frameBufferSize = (int)(_frameDurationMs / 1000.0f * _stream.Layout.ChannelCount * _stream.SampleRate); // Ring/frame buffer allocation _ring = new RingBuffer <float>(ringBufferSize); _frameBuffer = new float[frameBufferSize]; // Start streaming. _stream.Start(); } catch { // Dispose the stream on an exception. _stream?.Dispose(); _stream = null; _device?.Dispose(); _device = null; throw; } }
// Factory method public static InputDeviceHandle CreateAndOwn(SoundIO.Device device) => new InputDeviceHandle(device);
public InputStream(SoundIO.Device device) { _self = GCHandle.Alloc(this); _device = device; OpenStream(); }