public void Dispose() { _instream?.Dispose(); _instream = null; Device?.RemoveReference(); Device = null; _api?.Disconnect(); _api?.Dispose(); }
private void initInternal(AudioFormat format) { if (Device == null) { throw new Exception("No device is selected"); } if (Device.ProbeError != 0) { throw new Exception($"Probe Error : {Device.ProbeError}"); } var native = Soundio.ToSoundioFormat(format); if (!native.HasValue) { throw new NotSupportedException("Format is not supported : " + format); } _instream = Device.CreateInStream(); _instream.Format = native.Value; _instream.SampleRate = format.SampleRate; _instream.ReadCallback = ReadCallback; _instream.OverflowCallback = () => Overflow?.Invoke(this, EventArgs.Empty); _instream.ErrorCallback = () => UnrecoverableError?.Invoke(this, EventArgs.Empty); _instream.SoftwareLatency = DesiredLatency.TotalSeconds; _instream.Open(); // Open後にチャンネルは設定しないと動作しない模様 if (Device.CurrentLayout.ChannelCount != format.Channels) { checkFormatInternal(format, out var channelLayout); if (!channelLayout.HasValue) { throw new NotSupportedException("No suitable channel layout found : " + format.Channels); } _instream.Layout = channelLayout.Value; } _instream.SoftwareLatency = DesiredLatency.TotalSeconds; Format = Soundio.ToManagedFormat(_instream.Format, _instream.SampleRate, _instream.Layout.ChannelCount); SoftwareLatency = TimeSpan.FromSeconds(_instream.SoftwareLatency); var bytesPerSample = _instream.BytesPerSample; var capacity = Format.SampleRate * Format.Channels * bytesPerSample * _bufferDuration.TotalSeconds; _ringBuffer = new RingBuffer <byte>((uint)capacity); }
static void ReadCallback(SoundIOInStream instream, int frameCountMin, int frameCountMax) { SoundIORingBuffer ringBuffer = ringBuffers[instream.UserData]; SoundIOChannelAreas areas; SoundIoError err; IntPtr writePtr = ringBuffer.WritePointer; int freeBytes = ringBuffer.FreeCount; int freeCount = freeBytes / instream.BytesPerFrame; if (frameCountMin > freeCount) { throw new SoundIOException("Ring buffer overflow"); } int writeFrames = Math.Min(freeCount, frameCountMax); int framesLeft = writeFrames; while (true) { int frameCount = framesLeft; err = instream.BeginRead(out areas, ref frameCount); if (err != SoundIoError.None) { throw new SoundIOException(string.Format("Begin read error: {0}.", err.GetErrorMessage())); } if (frameCount == 0) { break; } if (areas == null) { // Due to an overflow there is a hole. Fill the ring buffer with // silence for the size of the hole. int count = frameCount * instream.BytesPerFrame; for (int i = 0; i < count; i++) { Marshal.WriteByte(writePtr + i, 0); } } else { int channelCount = instream.Layout.ChannelCount; int bytesPerSample = instream.BytesPerSample; for (int frame = 0; frame < frameCount; frame++) { for (int ch = 0; ch < channelCount; ch++) { unsafe { Buffer.MemoryCopy((void *)areas[ch].Pointer, (void *)writePtr, bytesPerSample, bytesPerSample); } areas[ch].AdvancePointer(); writePtr += bytesPerSample; } } } err = instream.EndRead(); if (err != SoundIoError.None) { throw new SoundIOException(string.Format("End read error: {0}.", err.GetErrorMessage())); } framesLeft -= frameCount; if (framesLeft <= 0) { break; } } int advanceBytes = writeFrames * instream.BytesPerFrame; ringBuffer.AdvanceWritePointer(advanceBytes); }
public static int Main(string[] args) { string exe = "SoundIORecord"; SoundIoBackend backend = SoundIoBackend.None; string deviceId = null; bool isRaw = true; string outfile = "1.pcm"; SoundIoError err; for (int i = 0; i < args.Length; i++) { string arg = args[i]; if (arg.StartsWith("--")) { if (arg.CompareTo("--raw") == 0) { isRaw = true; } else if (++i > args.Length) { return(Usage(exe)); } else if (arg.CompareTo("--backend") == 0) { backend = (SoundIoBackend)Enum.Parse(typeof(SoundIoBackend), args[i]); } else if (arg.CompareTo("--device") == 0) { deviceId = args[i]; } else { return(Usage(exe)); } } else if (outfile == null) { outfile = arg; } else { return(Usage(exe)); } } if (outfile == null) { return(Usage(exe)); } using (SoundIO soundIo = new SoundIO()) { err = (backend == SoundIoBackend.None) ? soundIo.Connect() : soundIo.ConnectBackend(backend); if (err != SoundIoError.None) { Console.Error.WriteLine("Error connecting: {0}.", err.GetErrorMessage()); return(1); } soundIo.FlushEvents(); SoundIODevice selectedDevice = null; if (deviceId != null) { foreach (var dev in soundIo) { if (dev.Aim == SoundIoDeviceAim.Input && dev.Id.Equals(deviceId) && dev.IsRaw == isRaw) { selectedDevice = dev; break; } } if (selectedDevice == null) { Console.Error.WriteLine("Invalid device id: {0}.", deviceId); return(1); } selectedDevice.AddRef(); // Enumerator cleans up itself on dispose } else { selectedDevice = soundIo.GetDefaultInputDevice(); if (selectedDevice == null) { Console.Error.WriteLine("No input devices available."); return(1); } } Console.WriteLine("Device: {0}.", selectedDevice.Name); if (selectedDevice.ProbeError != 0) { Console.Error.WriteLine("Unable to probe device: {0}.", selectedDevice.ProbeError.GetErrorMessage()); return(1); } selectedDevice.SortChannelLayouts(); int sampleRate = prioritizedSampleRates.FirstOrDefault(sr => selectedDevice.SupportsSampleRate(sr)); if (sampleRate == 0) { sampleRate = selectedDevice.SampleRates[0].Max; } SoundIoFormat fmt = prioritizedFormats.FirstOrDefault(f => selectedDevice.SupportsFormat(f)); if (fmt == SoundIoFormat.Invalid) { fmt = selectedDevice.Formats[0]; } using (SoundIOInStream instream = new SoundIOInStream(selectedDevice)) { instream.Format = fmt; instream.SampleRate = sampleRate; instream.OnReadCallback = ReadCallback; instream.OnOverflowCallback = OverflowCallback; err = instream.Open(); if (err != SoundIoError.None) { Console.Error.WriteLine("Unable to open input stream: {0}.", err.GetErrorMessage()); return(1); } Console.WriteLine("{0} {1}Hz {2} interleaved", instream.Layout.Name, sampleRate, fmt.GetFormatString()); const int ringBufferDurationSeconds = 30; int capacity = ringBufferDurationSeconds * instream.SampleRate * instream.BytesPerFrame; SoundIORingBuffer ringBuffer = new SoundIORingBuffer(soundIo, capacity); instream.UserData = ringBuffer.Handle; ringBuffers.Add(ringBuffer.Handle, ringBuffer); err = instream.Start(); if (err != SoundIoError.None) { Console.Error.WriteLine("Unable to start input device: {0}.", err.GetErrorMessage()); return(1); } Console.WriteLine("Recording data for 10 seconds."); int timeout = 100; using (var fs = File.OpenWrite(outfile)) { byte[] buffer = new byte[capacity]; while (true) // No memory allocations allowed { soundIo.FlushEvents(); Thread.Sleep(100); int fillBytes = ringBuffer.FillCount; IntPtr readBuf = ringBuffer.ReadPointer; Marshal.Copy(readBuf, buffer, 0, fillBytes); fs.Write(buffer, 0, fillBytes); ringBuffer.AdvanceReadPointer(fillBytes); if (--timeout <= 0) { break; } } } } selectedDevice.Release(); return(0); } }
static void OverflowCallback(SoundIOInStream instream) { Console.Error.WriteLine("Overflow {0}.", overflowCount++); }
static void overflow_callback(SoundIOInStream instream) { Console.Error.WriteLine("overflow {0}", overflow_callback_count++); }
static void read_callback(SoundIOInStream instream, int frame_count_min, int frame_count_max) { var write_ptr = ring_buffer.WritePointer; int free_bytes = ring_buffer.FreeCount; int free_count = free_bytes / instream.BytesPerFrame; if (frame_count_min > free_count) { throw new InvalidOperationException("ring buffer overflow"); // panic() } int write_frames = Math.Min(free_count, frame_count_max); int frames_left = write_frames; for (; ;) { int frame_count = frames_left; var areas = instream.BeginRead(ref frame_count); if (frame_count == 0) { break; } if (areas.IsEmpty) { // Due to an overflow there is a hole. Fill the ring buffer with // silence for the size of the hole. for (int i = 0; i < frame_count * instream.BytesPerFrame; i++) { Marshal.WriteByte(write_ptr + i, 0); } Console.Error.WriteLine("Dropped {0} frames due to internal overflow", frame_count); } else { for (int frame = 0; frame < frame_count; frame += 1) { int chCount = instream.Layout.ChannelCount; int copySize = instream.BytesPerSample; unsafe { for (int ch = 0; ch < chCount; ch += 1) { var area = areas.GetArea(ch); Buffer.MemoryCopy((void *)area.Pointer, (void *)write_ptr, copySize, copySize); area.Pointer += area.Step; write_ptr += copySize; } } } } instream.EndRead(); frames_left -= frame_count; if (frames_left <= 0) { break; } } int advance_bytes = write_frames * instream.BytesPerFrame; ring_buffer.AdvanceWritePointer(advance_bytes); }