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 device_id = null; string backend_name = null; bool raw = false; string outfile = null; foreach (var arg in args) { switch (arg) { case "--raw": raw = true; continue; default: if (arg.StartsWith("--backend:")) { backend_name = arg.Substring(arg.IndexOf(':') + 1); } else if (arg.StartsWith("--device:")) { device_id = arg.Substring(arg.IndexOf(':') + 1); } else { outfile = arg; } continue; } } var api = new SoundIO(); var backend = backend_name == null ? SoundIOBackend.None : (SoundIOBackend)Enum.Parse(typeof(SoundIOBackend), backend_name); if (backend == SoundIOBackend.None) { api.Connect(); } else { api.ConnectBackend(backend); } Console.WriteLine("backend: " + api.CurrentBackend); api.FlushEvents(); var device = device_id == null?api.GetInputDevice(api.DefaultInputDeviceIndex) : Enumerable.Range(0, api.InputDeviceCount) .Select(i => api.GetInputDevice(i)) .FirstOrDefault(d => d.Id == device_id && d.IsRaw == raw); if (device == null) { Console.Error.WriteLine("device " + device_id + " not found."); return(1); } Console.WriteLine("device: " + device.Name); if (device.ProbeError != 0) { Console.Error.WriteLine("Cannot probe device " + device_id + "."); return(1); } var sample_rate = prioritized_sample_rates.First(sr => device.SupportsSampleRate(sr)); var fmt = prioritized_formats.First(f => device.SupportsFormat(f)); var instream = device.CreateInStream(); instream.Format = fmt; instream.SampleRate = sample_rate; instream.ReadCallback = (fmin, fmax) => read_callback(instream, fmin, fmax); instream.OverflowCallback = () => overflow_callback(instream); instream.Open(); const int ring_buffer_duration_seconds = 30; int capacity = (int)(ring_buffer_duration_seconds * instream.SampleRate * instream.BytesPerFrame); ring_buffer = api.CreateRingBuffer(capacity); var buf = ring_buffer.WritePointer; instream.Start(); Console.WriteLine("Type CTRL+C to quit by killing process..."); using (var fs = File.OpenWrite(outfile)) { var arr = new byte [capacity]; unsafe { fixed(void *arrptr = arr) { for (; ;) { api.FlushEvents(); Thread.Sleep(1000); int fill_bytes = ring_buffer.FillCount; var read_buf = ring_buffer.ReadPointer; Buffer.MemoryCopy((void *)read_buf, arrptr, fill_bytes, fill_bytes); fs.Write(arr, 0, fill_bytes); ring_buffer.AdvanceReadPointer(fill_bytes); } } } } instream.Dispose(); device.RemoveReference(); api.Dispose(); return(0); }
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); } }
public static int Main(string [] args) { string in_device_id = null, out_device_id = null; string backend_name = null; bool in_raw = false, out_raw = false; double microphone_latency = 0.2; // seconds foreach (var arg in args) { switch (arg) { case "--in_raw": in_raw = true; continue; case "--out_raw": out_raw = true; continue; default: if (arg.StartsWith("--backend:")) { backend_name = arg.Substring(arg.IndexOf(':') + 1); } else if (arg.StartsWith("--in-device:")) { in_device_id = arg.Substring(arg.IndexOf(':') + 1); } else if (arg.StartsWith("--out-device:")) { out_device_id = arg.Substring(arg.IndexOf(':') + 1); } else if (arg.StartsWith("--latency:")) { microphone_latency = double.Parse(arg.Substring(arg.IndexOf(':') + 1)); } continue; } } var api = new SoundIO(); var backend = backend_name == null ? SoundIOBackend.None : (SoundIOBackend)Enum.Parse(typeof(SoundIOBackend), backend_name); if (backend == SoundIOBackend.None) { api.Connect(); } else { api.ConnectBackend(backend); } Console.WriteLine("backend: " + api.CurrentBackend); api.FlushEvents(); var in_device = in_device_id == null?api.GetInputDevice(api.DefaultInputDeviceIndex) : Enumerable.Range(0, api.InputDeviceCount) .Select(i => api.GetInputDevice(i)) .FirstOrDefault(d => d.Id == in_device_id && d.IsRaw == in_raw); if (in_device == null) { Console.Error.WriteLine("Input device " + in_device_id + " not found."); return(1); } Console.WriteLine("input device: " + in_device.Name); if (in_device.ProbeError != 0) { Console.Error.WriteLine("Cannot probe input device " + in_device_id + "."); return(1); } var out_device = out_device_id == null?api.GetOutputDevice(api.DefaultOutputDeviceIndex) : Enumerable.Range(0, api.OutputDeviceCount) .Select(i => api.GetOutputDevice(i)) .FirstOrDefault(d => d.Id == out_device_id && d.IsRaw == out_raw); if (out_device == null) { Console.Error.WriteLine("Output device " + out_device_id + " not found."); return(1); } Console.WriteLine("output device: " + out_device.Name); if (out_device.ProbeError != 0) { Console.Error.WriteLine("Cannot probe output device " + out_device_id + "."); return(1); } out_device.SortDeviceChannelLayouts(); var layout = SoundIODevice.BestMatchingChannelLayout(out_device, in_device); if (layout.IsNull) { throw new InvalidOperationException("channel layouts not compatible"); // panic() } var sample_rate = prioritized_sample_rates.FirstOrDefault(sr => in_device.SupportsSampleRate(sr) && out_device.SupportsSampleRate(sr)); if (sample_rate == default(int)) { throw new InvalidOperationException("incompatible sample rates"); // panic() } var fmt = prioritized_formats.FirstOrDefault(f => in_device.SupportsFormat(f) && out_device.SupportsFormat(f)); if (fmt == default(SoundIOFormat)) { throw new InvalidOperationException("incompatible sample formats"); // panic() } var instream = in_device.CreateInStream(); instream.Format = fmt; instream.SampleRate = sample_rate; instream.Layout = layout; instream.SoftwareLatency = microphone_latency; instream.ReadCallback = (fmin, fmax) => read_callback(instream, fmin, fmax); instream.Open(); var outstream = out_device.CreateOutStream(); outstream.Format = fmt; outstream.SampleRate = sample_rate; outstream.Layout = layout; outstream.SoftwareLatency = microphone_latency; outstream.WriteCallback = (fmin, fmax) => write_callback(outstream, fmin, fmax); outstream.UnderflowCallback = () => underflow_callback(outstream); outstream.Open(); int capacity = (int)(microphone_latency * 2 * instream.SampleRate * instream.BytesPerFrame); ring_buffer = api.CreateRingBuffer(capacity); var buf = ring_buffer.WritePointer; int fill_count = (int)(microphone_latency * outstream.SampleRate * outstream.BytesPerFrame); // FIXME: there should be more efficient way for memset() for (int i = 0; i < fill_count; i++) { Marshal.WriteByte(buf, i, 0); } ring_buffer.AdvanceWritePointer(fill_count); instream.Start(); outstream.Start(); for (;;) { api.WaitEvents(); } outstream.Dispose(); instream.Dispose(); in_device.RemoveReference(); out_device.RemoveReference(); api.Dispose(); return(0); }